Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose JointStyle and EndCapStyle on IPen #185

Merged

Conversation

jtjackson
Copy link
Contributor

@jtjackson jtjackson commented Dec 18, 2021

Prerequisites

  • I have written a descriptive pull-request title
  • I have verified that there are no overlapping pull-requests open
  • I have verified that I am following matches the existing coding patterns and practice as demonstrated in the repository. These follow strict Stylecop rules 👮.
  • I have provided test coverage for my change (where applicable)

Description

My implementation of exposing two properties on IPen. Addresses #158
I still need to add some tests to cover the new properties exposed here.

This is my first attempt at contributing to ImageSharp so be patient with me. 🙂

Any feedback is appreciated.

@CLAassistant
Copy link

CLAassistant commented Dec 18, 2021

CLA assistant check
All committers have signed the CLA.

@codecov
Copy link

codecov bot commented Dec 22, 2021

Codecov Report

Merging #185 (fb7d82c) into master (879f3cf) will increase coverage by 0%.
The diff coverage is 96%.

Impacted file tree graph

@@          Coverage Diff          @@
##           master   #185   +/-   ##
=====================================
  Coverage      70%    70%           
=====================================
  Files          87     87           
  Lines        5116   5117    +1     
  Branches     1062   1062           
=====================================
+ Hits         3595   3628   +33     
+ Misses       1307   1279   -28     
+ Partials      214    210    -4     
Flag Coverage Δ
unittests 70% <96%> (+<1%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
...ImageSharp.Drawing/Shapes/OutlinePathExtensions.cs 96% <90%> (-1%) ⬇️
src/ImageSharp.Drawing/Processing/Pen.cs 100% <100%> (ø)
...Processing/Processors/Drawing/DrawPathProcessor.cs 100% <100%> (ø)
...ssing/Processors/Text/DrawTextProcessor{TPixel}.cs 97% <100%> (ø)
...arp.Drawing/Shapes/PolygonClipper/ClipperOffset.cs 100% <100%> (+12%) ⬆️
src/ImageSharp.Drawing/Shapes/Clipper/clipper.cs 50% <0%> (+1%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 879f3cf...fb7d82c. Read the comment docs.

/// <summary>
/// Gets the stroke joint style
/// </summary>
public JointStyle StrokeJoint { get; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tend to favor using the actual enum type name as the property name. Means one less thing to remember...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, usually, I keep the enum type name and property name separate, but as it seems we are only using this once, I'm fine with matching them up here. I'm not feeling creative right now. 🙂

/// <param name="patternSectionCapStyle">The style to render between sections of the specified pattern.</param>
/// <returns>A new path representing the outline.</returns>
/// <exception cref="ClipperException">Couldn't calculate offset.</exception>
public static IPath GenerateOutline(this IPath path, float width, ReadOnlySpan<float> pattern, JointStyle jointStyle, EndCapStyle patternSectionCapStyle)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll need to review these extension methods to ensure that the method chaining covers all sensible combinations. I've done others in the library but not this one yet I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what I've been trying to figure out. I'm still navigating the tests that we have and determining what I need to do to add to the test coverage.

I know that there's a GenerateOutline usage that you referred to in #155 (comment):

private static void OutputStarOutlineDashed(int points, float inner = 10, float outer = 20, float width = 5, JointStyle jointStyle = JointStyle.Miter, EndCapStyle cap = EndCapStyle.Butt)
{
// center the shape outerRadii + 10 px away from edges
float offset = outer + 10;
var star = new Star(offset, offset, points, inner, outer);
IPath outline = star.GenerateOutline(width, new float[] { 3, 3 }, false, jointStyle, cap);
outline.SaveImage("Stars", $"StarOutlineDashed_{points}_{jointStyle}_{cap}.png");
}

So, I'm trying to wrap my head around how we can use these new extension methods. Should I add more to the sample program?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if they inherit well enough then coverage will be implicit. I'll pull down your branch and have a look.

@jtjackson
Copy link
Contributor Author

My apologies. I didn't see the comments until now. :(

I'll follow up tonight. Thanks!

@JimBobSquarePants
Copy link
Member

My apologies. I didn't see the comments until now. :(

No worries mate! We should all have been taking some time off anyway 😄

@jtjackson jtjackson marked this pull request as ready for review January 17, 2022 13:20
@jtjackson
Copy link
Contributor Author

I added some tests for the new properties. I also took the liberty of updating the sample code so it will use the updated GenerateOutline extension method.

The PR is ready for review. If there is anything I may have missed or overlooked, please let me know. :-)

@jtjackson
Copy link
Contributor Author

Looks like checks failed due to code coverage. I am not familiar with CodeCov here, so can someone clarify what is needed to make this pass?

@antonfirsov
Copy link
Member

@jtjackson you need to authenticate CodeCov with github to see the coverage change report. Coverage for a GenerateOutline overload call chain has been lost because you are no longer calling the top level overload. It's our fault that we don't have any direct tests for GenerateOutline, so don't worry about it

Copy link
Member

@antonfirsov antonfirsov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need tests validating the end-to end capability, meaning that the test should actually draw lines with custom JointStyle and EndCapStyle and demonstrate we can solve problems like #155.

You can take a look at DrawPathTests.cs. When running in Debug, the RunValidatingProcessorTestmethod should save debug output to tests/Images/ActualOutput/Drawing/DrawLinesTests/. Initially it will fail. If you are fine with the result output you can copy the images to the matching ReferenceOutput folder, making them pass. You should push the reference output to GIT, but make sure the images are not crazy huge.

Comment on lines 79 to 82
public JointStyle JointStyle { get; }

/// <inheritdoc/>
public EndCapStyle EndCap { get; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any ways to change these properties.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was following what was used for StokeWidth and StrokePattern properties. We can set the properties via extension methods like GenerateOutline.

Would it be ideal to be able to update the properties in a way like this:

Pen pen = new Pen();

pen.JointStyle = JointStyle.Miter;

Something like that?

Copy link
Member

@antonfirsov antonfirsov Jan 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We initialize StokeWidth and StrokePattern via costructor. If we want to follow existing patterns, we need to initialize it with in a new constructor overload.

Though I'm tempted to change the whole thing, make Pen non-readonly, removing all the constructors, and making every property settable.

@JimBobSquarePants @tocsoft thoughts?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pro: no constructor bloat
Con: loosing API symmetry with brushes which are readonly.

Copy link
Member

@antonfirsov antonfirsov Jan 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jtjackson don't be blocked by our API design discussion, just pick whatever solution you like, and go ahead with the tests. When we make our decision we can easily refactor.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah... I wanna do a usability review to make sure the APIs are where we need them to be. We can change things there.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened #195. Even if it's not where we land in the end, @jtjackson I recommend to go with the settable properties for JointStyle and EndCapStyle for simplicity.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wasn't sure if to reply here or create a new comment to help with the flow. I had made a comment but decided to move it to the reply here:

Just made the new properties settable. We can change this as needed based on the discussion in #195.

I also uploaded the reference images for the tests in DrawLinesTests.cs. I don't think it's too big (6 KB) but let me know if that's an issue.

It would be pretty too easy for me to go crazy and add a lot more tests (once a QA Engineer, always a QA engineer 🙂) but I think it's a good start unless we think we should add more. Right now, we can see where the JointStyle and EndCap properties are rendered based on the reference images (it's more clear with anti-alias turned on, I would think).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for more tests, but I would make the lines somewhat ticker for the JointStyle + EndCap tests for tow reasons:

  • Make the results more visible + auditable (currently the difference between JointStyle = Round vs Square is not visible)
  • Make sure it fails when there is a bug, even if the comparison is tolerant

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just updated the images and tests so that we should be able to discern the difference between styles. Let me know if it stands out to you.

One thing I noticed is that some tests failed locally but I can't tell if it's the change or it's my machine. I did try testing against master and it passed. 🤔 I'll debug some more when I get the chance.

Would it be possible for you to pull my changes and run tests on your machine? I suspect that the CI check will also let us know whether my changes passed muster.

@antonfirsov antonfirsov mentioned this pull request Jan 18, 2022
@jtjackson
Copy link
Contributor Author

I finally figured out why some of the tests were failing. It was because I added EndCapStyle parameter here:

public static IPath GenerateOutline(this IPath path, float width, ReadOnlySpan<float> pattern, bool startOff, JointStyle jointStyle = JointStyle.Square, EndCapStyle patternSectionCapStyle = EndCapStyle.Butt)
{
if (pattern.Length < 2)
{
return path.GenerateOutline(width, jointStyle: jointStyle, endCapStyle: patternSectionCapStyle);
}

Which would have a conflict with the call below because the "default" parameter appears to be EndCapStyle.Square:

public static IPath GenerateOutline(this IPath path, float width, JointStyle jointStyle = JointStyle.Square, EndCapStyle endCapStyle = EndCapStyle.Square)
{
var offset = new ClipperOffset(MiterOffsetDelta);
offset.AddPath(path, jointStyle, endCapStyle);
return offset.Execute(width);
}

According to the enum, the default is actually EndCapStyle.Butt. Any reason why it's square instead of butt in the last GenerateOutline method here?

I just removed the EndCapStyle parameter so that it would allow the tests to pass, but I want to make sure that to understand what's happening here. The reference images also have been updated and the comparison looks good but let me know.

…ink_A150_T5.png,tests/Images/ReferenceOutput/Drawing/DrawBezierTests/DrawBeziers_HotPink_A255_T5.png,tests/Images/ReferenceOutput/Drawing/DrawBezierTests/DrawBeziers_Red_A255_T3.png,tests/Images/ReferenceOutput/Drawing/DrawBezierTests/DrawBeziers_White_A255_T1.5.png,tests/Images/ReferenceOutput/Drawing/DrawBezierTests/DrawBeziers_White_A255_T15.png,tests/Images/ReferenceOutput/Drawing/DrawingRobustnessTests/LargeGeoJson_Mississippi_Lines_PixelOffset(0).png,tests/Images/ReferenceOutput/Drawing/DrawingRobustnessTests/LargeGeoJson_Mississippi_Lines_PixelOffset(5500).png,tests/Images/ReferenceOutput/Drawing/DrawLinesTests/DrawLines_Simple_Bgr24_Yellow_A(1)_T(10).png,tests/Images/ReferenceOutput/Drawing/DrawLinesTests/DrawLines_Simple_Rgba32_White_A(0.6)_T(10).png,tests/Images/ReferenceOutput/Drawing/DrawLinesTests/DrawLines_Simple_Rgba32_White_A(1)_T(2.5).png,tests/Images/ReferenceOutput/Drawing/DrawLinesTests/DrawLines_Simple_Rgba32_White_A(1)_T(5)_NoAntialias.png,tests/Images/ReferenceOutput/Drawing/DrawPathTests/PathExtendingOffEdgeOfImageShouldNotBeCropped.png: convert to Git LFS
@JimBobSquarePants
Copy link
Member

I just removed the EndCapStyle parameter so that it would allow the tests to pass, but I want to make sure that to understand what's happening here. The reference images also have been updated and the comparison looks good but let me know.

@jtjackson You've found a mistake. The default should always match the default in the enumeration.

I've pulled down your fork and have given OutlinePathExtensions a go-over to fix up the method inheritance and documentation. I've also fixes various LFS issues where images were not stored as LFS pointers.

I'll need to get push access to your fork to fix the LFS issues properly though so if you can do that please I can get this completed and merged.

@tocsoft
Copy link
Member

tocsoft commented Feb 8, 2022

The default needs to be square, if my memory serves it to match the default style from System.Drawing. The enum isn't one we originally created and that would be why the defaults miss match. However we should probably just change the enum to match the method.

@JimBobSquarePants
Copy link
Member

@jtjackson
Copy link
Contributor Author

I'll need to get push access to your fork to fix the LFS issues properly though so if you can do that please I can get this completed and merged.

@JimBobSquarePants I'm still learning more about how to do PRs on Github (I've done it on private repos, but not public) but it seems that if I checked Allow edit by maintainers, that should give you a push access to my fork?

At any rate, I've added you as a collaborator to my forked repo. Let me know if you have any issues. 🙂

@JimBobSquarePants
Copy link
Member

@jtjackson You did everything just fine, it's just there's known issues with Git LFS and GitHub that can in some scenarios be worked around but adding new LFS pointers seems not to be one of those scenarios. Only full repo access seems to provide what I need to push the new pointers.

Anyway, I'm very happy with these changes and the new tests look really good also. Thanks very much for your help 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants