Browse Source

Rotation around custom center point. Rename PrependCenteredRotationRadians back to PrependRotationRadians

af/merge-core
Anton Firszov 8 years ago
parent
commit
fb6c407026
  1. 40
      src/ImageSharp/Processing/AffineTransformBuilder.cs
  2. 35
      src/ImageSharp/Processing/ProjectiveTransformBuilder.cs
  3. 2
      tests/ImageSharp.Tests/Drawing/DrawImageTest.cs
  4. 8
      tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs
  5. 8
      tests/ImageSharp.Tests/Processing/Transforms/AffineTransformTests.cs
  6. 8
      tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs
  7. 46
      tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs

40
src/ImageSharp/Processing/AffineTransformBuilder.cs

@ -20,33 +20,55 @@ namespace SixLabors.ImageSharp.Processing
/// Prepends a centered rotation matrix using the given rotation in radians. /// Prepends a centered rotation matrix using the given rotation in radians.
/// </summary> /// </summary>
/// <param name="radians">The amount of rotation, in radians.</param> /// <param name="radians">The amount of rotation, in radians.</param>
/// <param name="centerPoint">The rotation center point</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder PrependCenteredRotationRadians(float radians) public AffineTransformBuilder PrependRotationRadians(float radians, Vector2 centerPoint)
=> this.Prepend(size => TransformUtils.CreateRotationMatrixRadians(radians, size)); => this.PrependMatrix(Matrix3x2.CreateRotation(radians, centerPoint));
/// <summary> /// <summary>
/// Appends a centered rotation matrix using the given rotation in radians. /// Appends a centered rotation matrix using the given rotation in radians.
/// </summary> /// </summary>
/// <param name="radians">The amount of rotation, in radians.</param> /// <param name="radians">The amount of rotation, in radians.</param>
/// <param name="centerPoint">The rotation center point</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder AppendRotationRadians(float radians, Vector2 centerPoint)
=> this.AppendMatrix(Matrix3x2.CreateRotation(radians, centerPoint));
/// <summary>
/// Prepends a rotation matrix using the given rotation angle in radians
/// and the image center point as rotation center.
/// </summary>
/// <param name="radians">The amount of rotation, in radians.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder PrependRotationRadians(float radians)
=> this.Prepend(size => TransformUtils.CreateRotationMatrixRadians(radians, size));
/// <summary>
/// Appends a rotation matrix using the given rotation angle in radians
/// and the image center point as rotation center.
/// </summary>
/// <param name="radians">The amount of rotation, in radians.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder AppendCenteredRotationRadians(float radians) public AffineTransformBuilder AppendRotationRadians(float radians)
=> this.Append(size => TransformUtils.CreateRotationMatrixRadians(radians, size)); => this.Append(size => TransformUtils.CreateRotationMatrixRadians(radians, size));
/// <summary> /// <summary>
/// Prepends a centered rotation matrix using the given rotation in degrees. /// Prepends a rotation matrix using the given rotation angle in degrees
/// and the image center point as rotation center.
/// </summary> /// </summary>
/// <param name="degrees">The amount of rotation, in degrees.</param> /// <param name="degrees">The amount of rotation, in degrees.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder PrependCenteredRotationDegrees(float degrees) public AffineTransformBuilder PrependRotationDegrees(float degrees)
=> this.PrependCenteredRotationRadians(ImageMaths.DegreesToRadians(degrees)); => this.PrependRotationRadians(ImageMaths.DegreesToRadians(degrees));
/// <summary> /// <summary>
/// Appends a centered rotation matrix using the given rotation in degrees. /// Appends a rotation matrix using the given rotation angle in degrees
/// and the image center point as rotation center.
/// </summary> /// </summary>
/// <param name="degrees">The amount of rotation, in degrees.</param> /// <param name="degrees">The amount of rotation, in degrees.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public AffineTransformBuilder AppendCenteredRotationDegrees(float degrees) public AffineTransformBuilder AppendRotationDegrees(float degrees)
=> this.AppendCenteredRotationRadians(ImageMaths.DegreesToRadians(degrees)); => this.AppendRotationRadians(ImageMaths.DegreesToRadians(degrees));
/// <summary> /// <summary>
/// Prepends a scale matrix from the given uniform scale. /// Prepends a scale matrix from the given uniform scale.

35
src/ImageSharp/Processing/ProjectiveTransformBuilder.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics; using System.Numerics;
using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.ImageSharp.Processing.Processors.Transforms;
@ -67,7 +68,7 @@ namespace SixLabors.ImageSharp.Processing
/// </summary> /// </summary>
/// <param name="radians">The amount of rotation, in radians.</param> /// <param name="radians">The amount of rotation, in radians.</param>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns> /// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder PrependCenteredRotationRadians(float radians) public ProjectiveTransformBuilder PrependRotationRadians(float radians)
{ {
var m = new Matrix4x4(TransformUtils.CreateRotationMatrixRadians(radians, this.Size)); var m = new Matrix4x4(TransformUtils.CreateRotationMatrixRadians(radians, this.Size));
return this.PrependMatrix(m); return this.PrependMatrix(m);
@ -78,27 +79,51 @@ namespace SixLabors.ImageSharp.Processing
/// </summary> /// </summary>
/// <param name="radians">The amount of rotation, in radians.</param> /// <param name="radians">The amount of rotation, in radians.</param>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns> /// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder AppendCenteredRotationRadians(float radians) public ProjectiveTransformBuilder AppendRotationRadians(float radians)
{ {
var m = new Matrix4x4(TransformUtils.CreateRotationMatrixRadians(radians, this.Size)); var m = new Matrix4x4(TransformUtils.CreateRotationMatrixRadians(radians, this.Size));
return this.AppendMatrix(m); return this.AppendMatrix(m);
} }
/// <summary>
/// Appends a centered rotation matrix using the given rotation in radians.
/// </summary>
/// <param name="radians">The amount of rotation, in radians.</param>
/// <param name="centerPoint">The rotation center.</param>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
internal ProjectiveTransformBuilder PrependRotationRadians(float radians, Vector2 centerPoint)
{
var m = Matrix4x4.CreateRotationZ(radians, new Vector3(centerPoint, 0));
return this.PrependMatrix(m);
}
/// <summary>
/// Appends a centered rotation matrix using the given rotation in radians.
/// </summary>
/// <param name="radians">The amount of rotation, in radians.</param>
/// <param name="centerPoint">The rotation center.</param>
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
internal ProjectiveTransformBuilder AppendRotationRadians(float radians, Vector2 centerPoint)
{
var m = Matrix4x4.CreateRotationZ(radians, new Vector3(centerPoint, 0));
return this.AppendMatrix(m);
}
/// <summary> /// <summary>
/// Prepends a centered rotation matrix using the given rotation in degrees. /// Prepends a centered rotation matrix using the given rotation in degrees.
/// </summary> /// </summary>
/// <param name="degrees">The amount of rotation, in degrees.</param> /// <param name="degrees">The amount of rotation, in degrees.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder PrependCenteredRotationDegrees(float degrees) public ProjectiveTransformBuilder PrependCenteredRotationDegrees(float degrees)
=> this.PrependCenteredRotationRadians(ImageMaths.DegreesToRadians(degrees)); => this.PrependRotationRadians(ImageMaths.DegreesToRadians(degrees));
/// <summary> /// <summary>
/// Appends a centered rotation matrix using the given rotation in degrees. /// Appends a centered rotation matrix using the given rotation in degrees.
/// </summary> /// </summary>
/// <param name="degrees">The amount of rotation, in degrees.</param> /// <param name="degrees">The amount of rotation, in degrees.</param>
/// <returns>The <see cref="AffineTransformBuilder"/>.</returns> /// <returns>The <see cref="AffineTransformBuilder"/>.</returns>
public ProjectiveTransformBuilder AppendCenteredRotationDegrees(float degrees) public ProjectiveTransformBuilder AppendRotationDegrees(float degrees)
=> this.AppendCenteredRotationRadians(ImageMaths.DegreesToRadians(degrees)); => this.AppendRotationRadians(ImageMaths.DegreesToRadians(degrees));
/// <summary> /// <summary>
/// Prepends a scale matrix from the given uniform scale. /// Prepends a scale matrix from the given uniform scale.

2
tests/ImageSharp.Tests/Drawing/DrawImageTest.cs

@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.Tests
using (var blend = Image.Load<TPixel>(TestFile.Create(TestImages.Bmp.Car).Bytes)) using (var blend = Image.Load<TPixel>(TestFile.Create(TestImages.Bmp.Car).Bytes))
{ {
AffineTransformBuilder builder = new AffineTransformBuilder() AffineTransformBuilder builder = new AffineTransformBuilder()
.AppendCenteredRotationDegrees(45F) .AppendRotationDegrees(45F)
.AppendScale(new SizeF(.25F, .25F)) .AppendScale(new SizeF(.25F, .25F))
.AppendTranslation(new PointF(10, 10)); .AppendTranslation(new PointF(10, 10));

8
tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs

@ -14,11 +14,15 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
{ {
protected override void AppendTranslation(AffineTransformBuilder builder, PointF translate) => builder.AppendTranslation(translate); protected override void AppendTranslation(AffineTransformBuilder builder, PointF translate) => builder.AppendTranslation(translate);
protected override void AppendScale(AffineTransformBuilder builder, SizeF scale) => builder.AppendScale(scale); protected override void AppendScale(AffineTransformBuilder builder, SizeF scale) => builder.AppendScale(scale);
protected override void AppendRotationRadians(AffineTransformBuilder builder, float radians) => builder.AppendCenteredRotationRadians(radians); protected override void AppendRotationRadians(AffineTransformBuilder builder, float radians) => builder.AppendRotationRadians(radians);
protected override void AppendRotationRadians(AffineTransformBuilder builder, float radians, Vector2 center) =>
builder.AppendRotationRadians(radians, center);
protected override void PrependTranslation(AffineTransformBuilder builder, PointF translate) => builder.PrependTranslation(translate); protected override void PrependTranslation(AffineTransformBuilder builder, PointF translate) => builder.PrependTranslation(translate);
protected override void PrependScale(AffineTransformBuilder builder, SizeF scale) => builder.PrependScale(scale); protected override void PrependScale(AffineTransformBuilder builder, SizeF scale) => builder.PrependScale(scale);
protected override void PrependRotationRadians(AffineTransformBuilder builder, float radians) => builder.PrependCenteredRotationRadians(radians); protected override void PrependRotationRadians(AffineTransformBuilder builder, float radians) => builder.PrependRotationRadians(radians);
protected override void PrependRotationRadians(AffineTransformBuilder builder, float radians, Vector2 center) =>
builder.PrependRotationRadians(radians, center);
protected override AffineTransformBuilder CreateBuilder(Rectangle rectangle) => new AffineTransformBuilder(); protected override AffineTransformBuilder CreateBuilder(Rectangle rectangle) => new AffineTransformBuilder();

8
tests/ImageSharp.Tests/Processing/Transforms/AffineTransformTests.cs

@ -80,7 +80,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
using (Image<TPixel> image = provider.GetImage()) using (Image<TPixel> image = provider.GetImage())
{ {
AffineTransformBuilder builder = new AffineTransformBuilder() AffineTransformBuilder builder = new AffineTransformBuilder()
.AppendCenteredRotationDegrees(30); .AppendRotationDegrees(30);
image.Mutate(c => c.Transform(builder, resampler)); image.Mutate(c => c.Transform(builder, resampler));
image.DebugSave(provider, resamplerName); image.DebugSave(provider, resamplerName);
@ -102,7 +102,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
{ {
image.DebugSave(provider, $"_original"); image.DebugSave(provider, $"_original");
AffineTransformBuilder builder = new AffineTransformBuilder() AffineTransformBuilder builder = new AffineTransformBuilder()
.AppendCenteredRotationDegrees(angleDeg) .AppendRotationDegrees(angleDeg)
.AppendScale(new SizeF(sx, sy)) .AppendScale(new SizeF(sx, sy))
.AppendTranslation(new PointF(tx, ty)); .AppendTranslation(new PointF(tx, ty));
@ -124,7 +124,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
using (Image<TPixel> image = provider.GetImage()) using (Image<TPixel> image = provider.GetImage())
{ {
AffineTransformBuilder builder = new AffineTransformBuilder() AffineTransformBuilder builder = new AffineTransformBuilder()
.AppendCenteredRotationDegrees(angleDeg) .AppendRotationDegrees(angleDeg)
.AppendScale(new SizeF(s, s)); .AppendScale(new SizeF(s, s));
image.Mutate(i => i.Transform(builder, KnownResamplers.Bicubic)); image.Mutate(i => i.Transform(builder, KnownResamplers.Bicubic));
@ -197,7 +197,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
using (Image<TPixel> image = provider.GetImage()) using (Image<TPixel> image = provider.GetImage())
{ {
AffineTransformBuilder builder = new AffineTransformBuilder() AffineTransformBuilder builder = new AffineTransformBuilder()
.AppendCenteredRotationDegrees(50) .AppendRotationDegrees(50)
.AppendScale(new SizeF(.6F, .6F)); .AppendScale(new SizeF(.6F, .6F));
image.Mutate(i => i.Transform(builder, sampler)); image.Mutate(i => i.Transform(builder, sampler));

8
tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs

@ -39,11 +39,15 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
protected override void AppendTranslation(ProjectiveTransformBuilder builder, PointF translate) => builder.AppendTranslation(translate); protected override void AppendTranslation(ProjectiveTransformBuilder builder, PointF translate) => builder.AppendTranslation(translate);
protected override void AppendScale(ProjectiveTransformBuilder builder, SizeF scale) => builder.AppendScale(scale); protected override void AppendScale(ProjectiveTransformBuilder builder, SizeF scale) => builder.AppendScale(scale);
protected override void AppendRotationRadians(ProjectiveTransformBuilder builder, float radians) => builder.AppendCenteredRotationRadians(radians); protected override void AppendRotationRadians(ProjectiveTransformBuilder builder, float radians) => builder.AppendRotationRadians(radians);
protected override void AppendRotationRadians(ProjectiveTransformBuilder builder, float radians, Vector2 center) =>
builder.AppendRotationRadians(radians, center);
protected override void PrependTranslation(ProjectiveTransformBuilder builder, PointF translate) => builder.PrependTranslation(translate); protected override void PrependTranslation(ProjectiveTransformBuilder builder, PointF translate) => builder.PrependTranslation(translate);
protected override void PrependScale(ProjectiveTransformBuilder builder, SizeF scale) => builder.PrependScale(scale); protected override void PrependScale(ProjectiveTransformBuilder builder, SizeF scale) => builder.PrependScale(scale);
protected override void PrependRotationRadians(ProjectiveTransformBuilder builder, float radians) => builder.PrependCenteredRotationRadians(radians); protected override void PrependRotationRadians(ProjectiveTransformBuilder builder, float radians) => builder.PrependRotationRadians(radians);
protected override void PrependRotationRadians(ProjectiveTransformBuilder builder, float radians, Vector2 center) =>
builder.PrependRotationRadians(radians, center);
protected override Vector2 Execute( protected override Vector2 Execute(
ProjectiveTransformBuilder builder, ProjectiveTransformBuilder builder,

46
tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs

@ -87,7 +87,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
[InlineData(200, 100, 10, 42, 84)] [InlineData(200, 100, 10, 42, 84)]
[InlineData(200, 100, 100, 42, 84)] [InlineData(200, 100, 100, 42, 84)]
[InlineData(100, 200, -10, 42, 84)] [InlineData(100, 200, -10, 42, 84)]
public void RotateDegrees_ShouldCreateCenteredMatrix(int width, int height, float deg, float x, float y) public void AppendRotationDegrees_WithoutSpecificRotationCenter_RotationIsCenteredAroundImageCenter(
int width,
int height,
float deg,
float x,
float y)
{ {
var size = new Size(width, height); var size = new Size(width, height);
TBuilder builder = this.CreateBuilder(size); TBuilder builder = this.CreateBuilder(size);
@ -97,13 +102,41 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
// TODO: We should also test CreateRotationMatrixDegrees() (and all TransformUtils stuff!) for correctness // TODO: We should also test CreateRotationMatrixDegrees() (and all TransformUtils stuff!) for correctness
Matrix3x2 matrix = TransformUtils.CreateRotationMatrixDegrees(deg, size); Matrix3x2 matrix = TransformUtils.CreateRotationMatrixDegrees(deg, size);
var position = new Vector2(x, y); var position = new Vector2(x, y);
var expected = Vector2.Transform(position, matrix); var expected = Vector2.Transform(position, matrix);
Vector2 actual = this.Execute(builder, new Rectangle(Point.Empty, size), position); Vector2 actual = this.Execute(builder, new Rectangle(Point.Empty, size), position);
Assert.Equal(actual, expected, Comparer); Assert.Equal(actual, expected, Comparer);
} }
[Theory]
[InlineData(200, 100, 10, 30, 61, 42, 84)]
[InlineData(200, 100, 100, 30, 10, 20, 84)]
[InlineData(100, 200, -10, 30, 20, 11, 84)]
public void AppendRotationDegrees_WithRotationCenter(
int width,
int height,
float deg,
float cx,
float cy,
float x,
float y)
{
var size = new Size(width, height);
TBuilder builder = this.CreateBuilder(size);
var centerPoint = new Vector2(cx, cy);
this.AppendRotationDegrees(builder, deg, centerPoint);
var matrix = Matrix3x2.CreateRotation(ImageMaths.DegreesToRadians(deg), centerPoint);
var position = new Vector2(x, y);
var expected = Vector2.Transform(position, matrix);
Vector2 actual = this.Execute(builder, new Rectangle(Point.Empty, size), position);
Assert.Equal(actual, expected, Comparer);
}
[Fact] [Fact]
public void AppendPrependOpposite() public void AppendPrependOpposite()
{ {
@ -116,10 +149,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
// Forwards // Forwards
this.AppendRotationRadians(b1, pi); this.AppendRotationRadians(b1, pi);
this.AppendScale(b1, new SizeF(2, 0.5f)); this.AppendScale(b1, new SizeF(2, 0.5f));
this.AppendRotationRadians(b1, pi / 2, new Vector2(-0.5f, -0.1f));
this.AppendTranslation(b1, new PointF(123, 321)); this.AppendTranslation(b1, new PointF(123, 321));
// Backwards // Backwards
this.PrependTranslation(b2, new PointF(123, 321)); this.PrependTranslation(b2, new PointF(123, 321));
this.PrependRotationRadians(b2, pi / 2, new Vector2(-0.5f, -0.1f));
this.PrependScale(b2, new SizeF(2, 0.5f)); this.PrependScale(b2, new SizeF(2, 0.5f));
this.PrependRotationRadians(b2, pi); this.PrependRotationRadians(b2, pi);
@ -152,14 +187,19 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
protected abstract void AppendTranslation(TBuilder builder, PointF translate); protected abstract void AppendTranslation(TBuilder builder, PointF translate);
protected abstract void AppendScale(TBuilder builder, SizeF scale); protected abstract void AppendScale(TBuilder builder, SizeF scale);
protected abstract void AppendRotationRadians(TBuilder builder, float radians); protected abstract void AppendRotationRadians(TBuilder builder, float radians);
protected abstract void AppendRotationRadians(TBuilder builder, float radians, Vector2 center);
protected abstract void PrependTranslation(TBuilder builder, PointF translate); protected abstract void PrependTranslation(TBuilder builder, PointF translate);
protected abstract void PrependScale(TBuilder builder, SizeF scale); protected abstract void PrependScale(TBuilder builder, SizeF scale);
protected abstract void PrependRotationRadians(TBuilder builder, float radians); protected abstract void PrependRotationRadians(TBuilder builder, float radians);
protected abstract void PrependRotationRadians(TBuilder b1, float v, Vector2 vector2);
protected virtual void AppendRotationDegrees(TBuilder builder, float degrees) => protected virtual void AppendRotationDegrees(TBuilder builder, float degrees) =>
this.AppendRotationRadians(builder, ImageMaths.DegreesToRadians(degrees)); this.AppendRotationRadians(builder, ImageMaths.DegreesToRadians(degrees));
protected virtual void AppendRotationDegrees(TBuilder builder, float degrees, Vector2 center) =>
this.AppendRotationRadians(builder, ImageMaths.DegreesToRadians(degrees), center);
protected abstract Vector2 Execute(TBuilder builder, Rectangle rectangle, Vector2 sourcePoint); protected abstract Vector2 Execute(TBuilder builder, Rectangle rectangle, Vector2 sourcePoint);
private static float Sqrt(float a) => (float)Math.Sqrt(a); private static float Sqrt(float a) => (float)Math.Sqrt(a);

Loading…
Cancel
Save