Skip to content

Commit

Permalink
Overhaul text arrangement and trimming (oxyplot#1531, oxyplot#1551, o…
Browse files Browse the repository at this point in the history
  • Loading branch information
VisualMelon committed May 16, 2020
1 parent 50e9eb0 commit 204cacc
Show file tree
Hide file tree
Showing 26 changed files with 1,722 additions and 463 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ All notable changes to this project will be documented in this file.
- Multi-Line Text support to SkiaRenderContext (#1538)
- Added title clipping to PlotModel (#1510)
- Added LabelStep and LabelSpacing to contour series (#1511)
- SvgExporter for OxyPlot.ImageSharp
- Consistent support for DrawText's maxSize parameter (#1559)

### Changed
- Legends model (#644)
Expand All @@ -48,6 +50,7 @@ All notable changes to this project will be documented in this file.
- SkiaRenderContext does not apply pixel snapping when rendering to vector graphic (#1539)
- Mark OxyPlot.PdfExporter and OxyPlot.Pdf.PdfExporter as obsolete (#1527)
- Replace Axis.DesiredSize by Axis.DesiredMargin, change signature of Axis.Measure(...) (#453)
- OxyPlot.Wpf to use common text arranging and trimming

### Removed
- Remove PlotModel.Legends (#644)
Expand All @@ -56,6 +59,7 @@ All notable changes to this project will be documented in this file.
- Remove exporter Background properties (#1409)
- Remove OxyThickness Width and Height properties (#1429)
- RenderingExtensions.DrawRectangleAsPolygon(...) extension methods. IRenderContext.DrawRectangle(...) with an appropriate EdgeRenderingMode can be used instead.
- UseVerticalAlignmentWorkaround property of OxyPlot.SvgRenderContext

### Fixed
- Legend font size is not affected by DefaultFontSize (#1396)
Expand All @@ -69,6 +73,9 @@ All notable changes to this project will be documented in this file.
- Text measurement and rendering in OxyPlot.ImageSharp
- ExampleLibrary reporting annotation-only PlotModels as transposable (#1544)
- Auto plot margin not taking width of labels into account (#453)
- Vertical text alignment in OxyPlot.SvgRenderContext (#1531)
- Consistent horizontal text alignment for mutliline text (#1554)
- Consistent implementation of MeasureText (#1551)

## [2.0.0] - 2019-10-19
### Added
Expand Down
54 changes: 54 additions & 0 deletions Source/Examples/ExampleLibrary/Examples/RenderingCapabilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,60 @@ public static PlotModel DrawMultilineTextAlignmentRotation()
return model;
}

/// <summary>
/// Shows max size capabilities for the DrawText method.
/// </summary>
/// <returns>A plot model.</returns>
[Example("DrawText - Bounded Multi-line Alignment/Rotation")]
public static PlotModel DrawBoundedMultilineTextAlignmentRotationWith()
{
var model = new PlotModel();
model.Annotations.Add(new DelegateAnnotation(rc =>
{
for (var ha = HorizontalAlignment.Left; ha <= HorizontalAlignment.Right; ha++)
{
for (var va = VerticalAlignment.Top; va <= VerticalAlignment.Bottom; va++)
{
var origin = new ScreenPoint(((int)ha + 2) * 170, ((int)va + 2) * 170);
rc.FillCircle(origin, 3, OxyColors.Blue, EdgeRenderingMode.Adaptive);
for (var rotation = 0; rotation < 360; rotation += 90)
{
rc.DrawText(origin, $"R{rotation:000}\n{ha}\n{va}", OxyColors.Black, fontSize: 20d, rotation: rotation, horizontalAlignment: ha, verticalAlignment: va, maxSize: new OxySize(50, 70));
}
}
}
}));
return model;
}

/// <summary>
/// Shows max size capabilities for the DrawText method.
/// </summary>
/// <returns>A plot model.</returns>
[Example("DrawText - Horizontal Text Trimming")]
public static PlotModel DrawHorizontalTextTrimmed()
{
var text = "This is a long piece of text with many words that barely qualifies as a sentence.";
var multiLineText = "This is a long piece of multiline text\nwith many words that\nbarely qualifies as a sentence.";

var model = new PlotModel();
model.Annotations.Add(new DelegateAnnotation(rc =>
{
for (int i = 0; i < 20; i++)
{
var p = new ScreenPoint(10, 10 + i * 20);
rc.DrawText(p, text, OxyColors.Black, maxSize: new OxySize(i * 20, double.MaxValue));
}
for (int i = 0; i < 5; i++)
{
var p = new ScreenPoint(10, 400 + i * 50);
rc.DrawText(p, multiLineText, OxyColors.Black, maxSize: new OxySize(i * 25, double.MaxValue));
}
}));
return model;
}

/// <summary>
/// Shows color capabilities for the DrawText method.
/// </summary>
Expand Down
12 changes: 11 additions & 1 deletion Source/OxyPlot.ImageSharp.Tests/PngExporterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,17 @@ public void LargeImageTest(bool interpolate)
public void TestMultilineAlignment()
{
var plotModel = ExampleLibrary.RenderingCapabilities.DrawMultilineTextAlignmentRotation();
var fileName = Path.Combine(this.outputDirectory, "Text.png");
var fileName = Path.Combine(this.outputDirectory, "Multiline-Alignment.png");
PngExporter.Export(plotModel, fileName, 700, 700);

Assert.IsTrue(File.Exists(fileName));
}

[Test]
public void TestBoundedMultilineAlignment()
{
var plotModel = ExampleLibrary.RenderingCapabilities.DrawBoundedMultilineTextAlignmentRotationWith();
var fileName = Path.Combine(this.outputDirectory, "Bounded-Multiline-Alignment.png");
PngExporter.Export(plotModel, fileName, 700, 700);

Assert.IsTrue(File.Exists(fileName));
Expand Down
209 changes: 209 additions & 0 deletions Source/OxyPlot.ImageSharp.Tests/SvgExporterTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="SvgExporterTests.cs" company="OxyPlot">
// Copyright (c) 2020 OxyPlot contributors
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

namespace OxyPlot.ImageSharp.Tests
{
using System;
using System.IO;
using NUnit.Framework;

using OxyPlot.Series;
using OxyPlot.ImageSharp;
using OxyPlot.Annotations;

[TestFixture]
public class SvgExporterTests
{
private const string SVG_FOLDER = "SVG";
private string outputDirectory;

[OneTimeSetUp]
public void Setup()
{
this.outputDirectory = Path.Combine(TestContext.CurrentContext.WorkDirectory, SVG_FOLDER);
Directory.CreateDirectory(this.outputDirectory);
}

[Test]
public void Export_SomeExamplesInExampleLibrary_CheckThatAllFilesExist()
{
var exporter = new SvgExporter(1000, 750);
var directory = Path.Combine(this.outputDirectory, "ExampleLibrary");
ExportTest.Export_FirstExampleOfEachExampleGroup_CheckThatAllFilesExist(exporter, directory, ".svg");
}

[Test]
public void ExportToStream()
{
var plotModel = CreateTestModel1();
var exporter = new SvgExporter(1000, 750);
var stream = new MemoryStream();
exporter.Export(plotModel, stream);

Assert.IsTrue(stream.Length > 0);
}

[Test]
public void ExportToFile()
{
var plotModel = CreateTestModel1();
var fileName = Path.Combine(this.outputDirectory, "Plot1.svg");
SvgExporter.Export(plotModel, fileName, 1000, 750);

Assert.IsTrue(File.Exists(fileName));
}

[Test]
public void ExportWithDifferentBackground()
{
var plotModel = CreateTestModel1();
plotModel.Background = OxyColors.Yellow;
var fileName = Path.Combine(this.outputDirectory, "Background_Yellow.svg");
var exporter = new SvgExporter(1000, 750);
using (var stream = File.OpenWrite(fileName))
{
exporter.Export(plotModel, stream);
}

Assert.IsTrue(File.Exists(fileName));
}

[Test]
[TestCase(0.75)]
[TestCase(1)]
[TestCase(1.2)]
[TestCase(2)]
[TestCase(3.1415)]
public void ExportWithResolution(double factor)
{
var resolution = (int)(96 * factor);
var plotModel = CreateTestModel1();
var directory = Path.Combine(this.outputDirectory, "Resolution");
Directory.CreateDirectory(directory);

var fileName = Path.Combine(directory, $"Resolution{resolution}.svg");
var exporter = new SvgExporter((int)(400 * factor), (int)(300 * factor), resolution);

using (var stream = File.OpenWrite(fileName))
{
exporter.Export(plotModel, stream);
}

Assert.IsTrue(File.Exists(fileName));
}

[Test]
[TestCase(true)]
[TestCase(false)]
public void PlotBackgroundImageTest(bool interpolate)
{
// this is a test of the DrawImage function; don't add pointless backgrounds to your plots

var plotModel = CreateTestModel1();

var pixelData = new OxyColor[5, 5];
for (int i = 0; i < pixelData.GetLength(0); i++)
{
for (int j = 0; j < pixelData.GetLength(1); j++)
{
pixelData[i, j] = OxyColor.FromArgb(255, 128, (byte)((i * 255) / pixelData.GetLength(0)), (byte)((j * 255) / pixelData.GetLength(1)));
}
}

var oxyImage = OxyImage.Create(pixelData, ImageFormat.Png);
var imageAnnotation = new ImageAnnotation()
{
ImageSource = oxyImage,
X = new PlotLength(-0.0, PlotLengthUnit.RelativeToPlotArea),
Y = new PlotLength(-0.0, PlotLengthUnit.RelativeToPlotArea),
Width = new PlotLength(1.0, PlotLengthUnit.RelativeToPlotArea),
Height = new PlotLength(1.0, PlotLengthUnit.RelativeToPlotArea),
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Top,
Interpolate = interpolate
};
plotModel.Annotations.Add(imageAnnotation);

var fileName = Path.Combine(this.outputDirectory, $"PlotBackground{(interpolate ? "Interpolated" : "Pixelated")}.svg");
var exporter = new SvgExporter(1000, 750);
using (var stream = File.OpenWrite(fileName))
{
exporter.Export(plotModel, stream);
}

Assert.IsTrue(File.Exists(fileName));
}

[Test]
[TestCase(true)]
[TestCase(false)]
public void LargeImageTest(bool interpolate)
{
// this is a test of the DrawImage function; don't add pointless backgrounds to your plots

var plotModel = CreateTestModel1();

var pixelData = new OxyColor[5, 5];
for (int i = 0; i < pixelData.GetLength(0); i++)
{
for (int j = 0; j < pixelData.GetLength(1); j++)
{
pixelData[i, j] = OxyColor.FromArgb(255, 128, (byte)((i * 255) / pixelData.GetLength(0)), (byte)((j * 255) / pixelData.GetLength(1)));
}
}

var oxyImage = OxyImage.Create(pixelData, ImageFormat.Png);
var imageAnnotation = new ImageAnnotation()
{
ImageSource = oxyImage,
X = new PlotLength(-1, PlotLengthUnit.RelativeToViewport),
Y = new PlotLength(-1, PlotLengthUnit.RelativeToViewport),
Width = new PlotLength(3, PlotLengthUnit.RelativeToViewport),
Height = new PlotLength(3, PlotLengthUnit.RelativeToViewport),
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Top,
Interpolate = interpolate
};
plotModel.Annotations.Add(imageAnnotation);

var fileName = Path.Combine(this.outputDirectory, $"LargeImage{(interpolate ? "Interpolated" : "Pixelated")}.svg");
var exporter = new SvgExporter(1000, 750);
using (var stream = File.OpenWrite(fileName))
{
exporter.Export(plotModel, stream);
}

Assert.IsTrue(File.Exists(fileName));
}

[Test]
public void TestMultilineAlignment()
{
var plotModel = ExampleLibrary.RenderingCapabilities.DrawMultilineTextAlignmentRotation();
var fileName = Path.Combine(this.outputDirectory, "Multiline-Alignment.svg");
SvgExporter.Export(plotModel, fileName, 700, 700);

Assert.IsTrue(File.Exists(fileName));
}

[Test]
public void TestBoundedMultilineAlignment()
{
var plotModel = ExampleLibrary.RenderingCapabilities.DrawBoundedMultilineTextAlignmentRotationWith();
var fileName = Path.Combine(this.outputDirectory, "Bounded-Multiline-Alignment.svg");
SvgExporter.Export(plotModel, fileName, 700, 700);

Assert.IsTrue(File.Exists(fileName));
}

private static PlotModel CreateTestModel1()
{
var model = new PlotModel { Title = "Test 1" };
model.Series.Add(new FunctionSeries(Math.Sin, 0, Math.PI * 8, 200, "sin(x)"));
return model;
}
}
}

0 comments on commit 204cacc

Please sign in to comment.