/// <summary> /// Draws the specified captions according to the specified user-specific position. /// </summary> private void DrawCaptions(RectangleRange rectangleRange, IEnumerable <ScopePositionCaption> captions, PointD position) { captions = captions ?? new ScopePositionCaption[0]; foreach (var caption in captions) { var alignmentDistance = _defaultCaptionsAlignmentDistance; switch (caption.AlignmentReference) { case ScopeAlignmentReference.YPositionAndHorizontalRangeEdge: alignmentDistance = caption.YieldToMarker ? new Distance(alignmentDistance.Dx, alignmentDistance.Dy + _lateralCursorMarkerSize) : alignmentDistance; DrawTextAtVerticalEdge(rectangleRange, caption.CurrentText, position.Y, caption.Color, caption.HorizontalAlignment, caption.VerticalAlignment, alignmentDistance); break; case ScopeAlignmentReference.XPositionAndVerticalRangeEdge: alignmentDistance = caption.YieldToMarker ? new Distance(alignmentDistance.Dx + _lateralCursorMarkerSize, alignmentDistance.Dy) : alignmentDistance; DrawTextAtHorizontalEdge(rectangleRange, caption.CurrentText, position.X, caption.Color, caption.HorizontalAlignment, caption.VerticalAlignment, alignmentDistance); break; default: DrawText(rectangleRange, caption.CurrentText, position, caption.Color, caption.HorizontalAlignment, caption.VerticalAlignment, alignmentDistance); break; } } }
/// <summary> /// Draws a scope cursor. /// </summary> private void DrawCursor(RectangleRange rectangleRange, ScopeCursor cursor) { using (CreateContextState()) { ClipToRange(rectangleRange); DrawCursorLinesAndMarkers(rectangleRange, cursor); foreach (var tick in cursor.XTicks ?? new ScopeCursorValueTick[0]) { var tickPosition = new PointD(tick.Value, cursor.Position.Y); DrawCursorTickLines(rectangleRange, tick, tickPosition, ScopeCursorLines.X, cursor.Color); DrawCaptions(rectangleRange, tick.Captions, tickPosition); } foreach (var tick in cursor.YTicks ?? new ScopeCursorValueTick[0]) { var tickPosition = new PointD(cursor.Position.X, tick.Value); DrawCursorTickLines(rectangleRange, tick, tickPosition, ScopeCursorLines.Y, cursor.Color); DrawCaptions(rectangleRange, tick.Captions, tickPosition); } DrawCaptions(rectangleRange, cursor.Captions, cursor.Position.CairoPoint); } }
/// <summary> /// Draws the lines of a cursor tick. /// </summary> private void DrawCursorTickLines(RectangleRange rectangleRange, ScopeCursorValueTick tick, PointD tickPosition, ScopeCursorLines tickLines, Color color) { var userToDeviceMatrix = rectangleRange.Matrix; var cursorTickHalfUserLengths = userToDeviceMatrix.TransformDistanceInverse(_cursorTickLength / 2, _cursorTickLength / 2); using (CreateContextState()) { Context.SetSourceColor(color); using (CreateContextState(userToDeviceMatrix)) { if ((tickLines & ScopeCursorLines.X) != ScopeCursorLines.None) { AddLinePath(new PointD(tickPosition.X, tickPosition.Y - cursorTickHalfUserLengths.Dy), new PointD(tickPosition.X, tickPosition.Y + cursorTickHalfUserLengths.Dy)); } } using (CreateContextState(userToDeviceMatrix)) { if ((tickLines & ScopeCursorLines.Y) != ScopeCursorLines.None) { AddLinePath(new PointD(tickPosition.X - cursorTickHalfUserLengths.Dx, tickPosition.Y), new PointD(tickPosition.X + cursorTickHalfUserLengths.Dx, tickPosition.Y)); } } Context.LineWidth = _cursorTickLineWidth; Context.Stroke(); } }
/// <summary> /// Draws a scope graph. /// </summary> private void DrawGraph(RectangleRange rectangleRange, ScopeGraph graph) { var lineType = graph.LineType; using (CreateContextState()) { ClipToRange(rectangleRange); switch (lineType) { case ScopeLineType.Dots: DrawGraph(rectangleRange, graph, ScopeLineType.Dots, _dotsGraphLineWidth); break; case ScopeLineType.LineAndDots: DrawGraph(rectangleRange, graph, ScopeLineType.Line, _reducedGraphLineWidth); DrawGraph(rectangleRange, graph, ScopeLineType.Dots, _dotsGraphLineWidth); break; default: DrawGraph(rectangleRange, graph, lineType, _graphStandardLineWidth); break; } } }
/// <summary> /// Draws a text aligned with a certain distance to the specified user-specific position. /// </summary> private void DrawText(RectangleRange rectangleRange, string text, PointD position, Color textColor, ScopeHorizontalAlignment horizontalAlignment, ScopeVerticalAlignment verticalAlignment, Distance alignmentDistance) { var devicePosition = rectangleRange.Matrix.TransformPoint(position); DrawText(text, devicePosition, textColor, horizontalAlignment, verticalAlignment, alignmentDistance); }
/// <summary> /// Draws a text aligned with a certain distance to the specified user-specific position /// at the horizontal edge. /// </summary> private void DrawTextAtHorizontalEdge(RectangleRange rectangleRange, string text, double xPosition, Color textColor, ScopeHorizontalAlignment horizontalAlignment, ScopeVerticalAlignment verticalAlignment, Distance alignmentDistance) { var yPosition = verticalAlignment == ScopeVerticalAlignment.Bottom ? rectangleRange.MinY : rectangleRange.MaxY; DrawText(rectangleRange, text, new PointD(xPosition, yPosition), textColor, horizontalAlignment, verticalAlignment, alignmentDistance); }
/// <summary> /// Clips any rendering to the specified range. /// </summary> private void ClipToRange(RectangleRange rectangleRange) { var userToDeviceMatrix = rectangleRange.Matrix; using (CreateContextState(userToDeviceMatrix)) { Context.MoveTo(rectangleRange.MinX, rectangleRange.MinY); Context.LineTo(rectangleRange.MaxX, rectangleRange.MinY); Context.LineTo(rectangleRange.MaxX, rectangleRange.MaxY); Context.LineTo(rectangleRange.MinX, rectangleRange.MaxY); Context.ClosePath(); } Context.Clip(); }
/// <summary> /// Draws a scope graph. /// </summary> private void DrawGraph(RectangleRange rectangleRange, ScopeGraph graph, ScopeLineType lineType, double lineWidth) { var userToDeviceMatrix = rectangleRange.Matrix; var vertices = graph.Vertices ?? new PointD[0]; var referencePoint = graph.ReferencePoint; var referencePointPosition = graph.ReferencePointPosition; var xScaleFactor = graph.XScaleFactor; var yScaleFactor = graph.YScaleFactor; var color = graph.Color; if (vertices.Any()) { using (CreateContextState()) { Context.SetSourceColor(color); // Create a new transformation matrix considering scale factors // and reference point position. var vertexMatrix = (Matrix)userToDeviceMatrix.Clone(); vertexMatrix.Translate(referencePointPosition.X, referencePointPosition.Y); vertexMatrix.Scale(xScaleFactor, yScaleFactor); vertexMatrix.Translate(-referencePoint.X, -referencePoint.Y); using (CreateContextState(vertexMatrix)) { Context.MoveTo(vertices.First()); foreach (var vertex in vertices) { if (lineType == ScopeLineType.Dots) { Context.MoveTo(vertex); } Context.LineTo(vertex); } } Context.LineWidth = lineWidth; if (lineType == ScopeLineType.Dots) { Context.LineCap = LineCap.Round; } Context.Stroke(); } } }
/// <summary> /// Draws the background. /// </summary> private void DrawGraticuleBackground(RectangleRange rectangleRange, Color backGroundColor) { var userToDeviceMatrix = rectangleRange.Matrix; using (CreateContextState()) { using (CreateContextState(userToDeviceMatrix)) { Context.MoveTo(rectangleRange.MinX, rectangleRange.MinY); Context.LineTo(rectangleRange.MaxX, rectangleRange.MinY); Context.LineTo(rectangleRange.MaxX, rectangleRange.MaxY); Context.LineTo(rectangleRange.MinX, rectangleRange.MaxY); Context.ClosePath(); } Context.SetSourceColor(backGroundColor); Context.Fill(); } }
/// <summary> /// Draws the scope graphics (i.e. the graticule and all its contents) to a rectangle range. /// </summary> /// <param name="userRange">The rectangle range to draw the contents to.</param> /// <param name="cursors">The cursors to draw.</param> public void DrawScopeGraphics(RectangleRange userRange, IEnumerable <ScopeCursor> cursors, IEnumerable <ScopeGraph> graphs) { var backGroundColor = new Color(0, 0.1, 0); var graticuleColor = new Color(0.5, 0.5, 0.5); DrawGraticuleBackground(userRange, backGroundColor); DrawGraticule(userRange, 1.0, 1.0, graticuleColor); graphs = graphs ?? new ScopeGraph[0]; foreach (var graph in graphs) { DrawGraph(userRange, graph); } cursors = cursors ?? new ScopeCursor[0]; foreach (var cursor in cursors) { DrawCursor(userRange, cursor); } }
/// <summary> /// Draws the lines and markers of a scope cursor. /// </summary> private void DrawCursorLinesAndMarkers(RectangleRange rectangleRange, ScopeCursor cursor) { var userToDeviceMatrix = rectangleRange.Matrix; var position = cursor.Position; var lines = cursor.Lines; var markers = cursor.Markers; var color = cursor.Color; using (CreateContextState()) { Context.SetSourceColor(color); using (CreateContextState(userToDeviceMatrix)) { if ((lines & ScopeCursorLines.X) != ScopeCursorLines.None) { AddLinePath(new PointD(position.X, rectangleRange.MinY), new PointD(position.X, rectangleRange.MaxY)); } } Context.LineWidth = (cursor.HighlightedLines & ScopeCursorLines.X) != ScopeCursorLines.None ? _cursorHighlightLineWidth : _cursorLineWidth; Context.SetDash( cursor.LineWeight == ScopeCursorLineWeight.Low ? _cursorLineLowWeightDashes : cursor.LineWeight == ScopeCursorLineWeight.Medium ? _cursorLineMediumWeightDashes : new double[0], 0); Context.Stroke(); using (CreateContextState(userToDeviceMatrix)) { if ((lines & ScopeCursorLines.Y) != ScopeCursorLines.None) { AddLinePath(new PointD(rectangleRange.MinX, position.Y), new PointD(rectangleRange.MaxX, position.Y)); } } Context.LineWidth = (cursor.HighlightedLines & ScopeCursorLines.Y) != ScopeCursorLines.None ? _cursorHighlightLineWidth : _cursorLineWidth; Context.Stroke(); if ((markers & ScopeCursorMarkers.XLeft) != ScopeCursorMarkers.None) { AddXCursorMarkerPairPathFromCurrentPosition( userToDeviceMatrix.TransformPoint(position.X, rectangleRange.MaxY), userToDeviceMatrix.TransformPoint(position.X, rectangleRange.MinY), ScopeHorizontalAlignment.Right); } if ((markers & ScopeCursorMarkers.XRight) != ScopeCursorMarkers.None) { AddXCursorMarkerPairPathFromCurrentPosition( userToDeviceMatrix.TransformPoint(position.X, rectangleRange.MaxY), userToDeviceMatrix.TransformPoint(position.X, rectangleRange.MinY), ScopeHorizontalAlignment.Left); } if ((markers & ScopeCursorMarkers.YUpper) != ScopeCursorMarkers.None) { AddYCursorMarkerPairPathFromCurrentPosition( userToDeviceMatrix.TransformPoint(rectangleRange.MinX, position.Y), userToDeviceMatrix.TransformPoint(rectangleRange.MaxX, position.Y), ScopeVerticalAlignment.Bottom); } if ((markers & ScopeCursorMarkers.YLower) != ScopeCursorMarkers.None) { AddYCursorMarkerPairPathFromCurrentPosition( userToDeviceMatrix.TransformPoint(rectangleRange.MinX, position.Y), userToDeviceMatrix.TransformPoint(rectangleRange.MaxX, position.Y), ScopeVerticalAlignment.Top); } Context.LineWidth = _cursorMarkersLineWidth; Context.FillPreserve(); Context.Stroke(); } }
/// <summary> /// Draws a scope graticule. /// </summary> private void DrawGraticule(RectangleRange rectangleRange, double xUserStepWidth, double yUserStepWidth, Color graticuleColor) { // Set ticks depending on axes style, double.NaN suppresses ticks. double minorAxesTicksPerUserStep = 10f; // double.NaN tu suppress ticks double majorAxesTicksPerUserStep = 2f; // double.NaN tu suppress ticks if (true) // true for ticks @ 0.20 intervals, no major ticks { minorAxesTicksPerUserStep = 5f; majorAxesTicksPerUserStep = double.NaN; } var minX = rectangleRange.MinX; var maxX = rectangleRange.MaxX; var minY = rectangleRange.MinY; var maxY = rectangleRange.MaxY; var userToDeviceMatrix = rectangleRange.Matrix; using (CreateContextState()) { using (CreateContextState(userToDeviceMatrix)) { var xTicksBaseLength = _ticksBaseLength; var yTicksBaseLength = _ticksBaseLength; if (!double.IsNaN(minorAxesTicksPerUserStep)) { var xMinorTicksStepWidth = xUserStepWidth / minorAxesTicksPerUserStep; ProcessSteps(minX, maxX, xMinorTicksStepWidth, current => AddLinePath(new PointD(current, -xTicksBaseLength), new PointD(current, xTicksBaseLength))); var yMinorTicksStepWidth = yUserStepWidth / minorAxesTicksPerUserStep; ProcessSteps(minY, maxY, yMinorTicksStepWidth, current => AddLinePath(new PointD(-yTicksBaseLength, current), new PointD(yTicksBaseLength, current))); } if (!double.IsNaN(majorAxesTicksPerUserStep)) { var xMajorTicksStepWidth = xUserStepWidth / majorAxesTicksPerUserStep; ProcessSteps(minX, maxX, xMajorTicksStepWidth, current => AddLinePath(new PointD(current, 2 * -xTicksBaseLength), new PointD(current, 2 * xTicksBaseLength))); var yMajorTicksStepWidth = yUserStepWidth / majorAxesTicksPerUserStep; ProcessSteps(minY, maxY, yMajorTicksStepWidth, current => AddLinePath(new PointD(2 * -yTicksBaseLength, current), new PointD(2 * yTicksBaseLength, current))); } } Context.SetSourceColor(graticuleColor); Context.LineWidth = _graticuleAxesTicksLineWidth; Context.Stroke(); using (CreateContextState(userToDeviceMatrix)) { ProcessSteps(minX, maxX, xUserStepWidth, current => AddLinePath(new PointD(current, minY), new PointD(current, maxY))); ProcessSteps(minY, maxY, yUserStepWidth, current => AddLinePath(new PointD(minX, current), new PointD(maxX, current))); } Context.LineWidth = _graticuleLineWidth; Context.Stroke(); using (CreateContextState(userToDeviceMatrix)) { AddLinePath(new PointD(0f, minY), new PointD(0f, maxY)); AddLinePath(new PointD(minX, 0f), new PointD(maxX, 0f)); } Context.LineWidth = _graticuleAxesLineWidth; Context.Stroke(); } }