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

Voronoi diagrams #692

Merged
merged 36 commits into from
Jan 21, 2024
Merged

Voronoi diagrams #692

merged 36 commits into from
Jan 21, 2024

Conversation

Lehonti
Copy link
Contributor

@Lehonti Lehonti commented Jan 14, 2024

Here's with Euclidean distance calculation:

image

And here's with Manhattan distance calculation:

image

For now it's only raster-based, but maybe at some point we could add a different algorithm that allows us to draw it based on shapes, and then having Cairo put everything together.

There may be a merge conflict in CoreEffectsExtension if we were to merge both this one and #457

Thank you for adding support for non-tileable effects, which made this possible.

@Lehonti
Copy link
Contributor Author

Lehonti commented Jan 14, 2024

I just made sure there aren't any potential merge conflicts

@Lehonti
Copy link
Contributor Author

Lehonti commented Jan 14, 2024

I just noticed that, with the current code, some colors tend to be to the left and some to the right. This is unintended, but at the same time it's cool, so it could be added as an option (besides the option of truly randomizing the position of the colors)

image

@Lehonti
Copy link
Contributor Author

Lehonti commented Jan 14, 2024

...And now the option has been added, along with the relevant captions and comments for translators

Copy link
Member

@cameronwhite cameronwhite left a comment

Choose a reason for hiding this comment

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

Looks cool!

I left a few comments from doing some initial testing. Please also add a unit test for this too when ready :)


Span<ColorBgra> dst_data = dst.GetPixelData ();

for (int y = roi.Top; y <= roi.Bottom; y++) {
Copy link
Member

Choose a reason for hiding this comment

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

The effect is non-fileable because of CreateSettings, correct? This loop could still be done in parallel for better performance

Copy link
Contributor Author

@Lehonti Lehonti Jan 15, 2024

Choose a reason for hiding this comment

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

Not only that, there is more to it.

Most effects use the entire canvas as the area where the effect is rendered, and the ROIs as the parts of the area that they have to fill.

On the other hand, in this effect, the ROI is the area, and the calculations are done based on the ROI.

For a more concrete example, open a photo with Pinta, select a rectangle area, and apply the twist effect to it. The center of the twist won't be the center of the selection, but the center of the canvas.

Maybe we should re-engineer the rendering and the tileability mechanisms now that the rendering API can still be changed, and adapt the algorithms so that they can act on either the entire canvas or the overall ROI (while still being executable in parallel).

Also, just to clarify, I understand what you mean: once we have the list of points and the list of colors, the color of each pixel in the rendering can be calculated independently, which means it can all be done in parallel. That much is clear... I just think it's essential to address the other part of it as well.

I dare say that we could delay the plugin feature by one release if that means that we get the API right. The current one is just very limited, and just like support for non-tileable effects required a change in the API, I am sure there are many other things that just are not possible as of now.

Copy link
Member

Choose a reason for hiding this comment

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

I agree that the API probably needs further thought to solve this in a better way.
For now I'd suggest still adding a Parallel.For loop to improve performance - the only downside is just that it won't be able to hook into the live preview to incrementally render tiles as they're completed.

FYI the next Pinta release is hopefully not too far away - basically once #674 is figured out I plan to start locking things down. There still can be API changes in the future if we decide they're necessary

Copy link
Contributor Author

@Lehonti Lehonti Jan 16, 2024

Choose a reason for hiding this comment

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

It's being done with Parallel now (with some adjustments, as a ReadOnlySpan<T> cannot be referenced inside a lambda. There is a noticeable speed improvement in my machine.

Copy link
Member

Choose a reason for hiding this comment

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

great, yeah it seems much faster on my machine too 👍

PointI pixelLocation = new (x, y);
double shortestDistance = double.MaxValue;
int closestIndex = 0;
for (var i = 0; i < settings.points.Length; i++) {
Copy link
Member

Choose a reason for hiding this comment

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

This is probably more something to leave a TODO comment for future work, but adding some acceleration structure here could improve performance when there is a large number of points


public sealed class VoronoiDiagramData : EffectData
{
[Caption ("Distance Calculation Method")]
Copy link
Member

Choose a reason for hiding this comment

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

I'd suggest calling this Distance Metric or Distance Method to be a bit more concise

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 went ahead and called it Distance Metric

public DistanceCalculationMethod DistanceCalculationMethod { get; set; } = DistanceCalculationMethod.Euclidean;

[Caption ("Point Count"), MinimumValue (1), MaximumValue (1024)]
public int PointCount { get; set; } = 100;
Copy link
Member

Choose a reason for hiding this comment

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

Would something like Number of Regions make more sense to a user, since they don't see the point positions?

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 named it Number of Cells

public int PointCount { get; set; } = 100;

// Translators: The user can choose whether or not to render the points used in the calculation of a Voronoi diagram
[Caption ("Show Points")]
Copy link
Member

Choose a reason for hiding this comment

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

Are there good use cases for this to be exposed to users? Seems more like a debugging option to me at first glance

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It could be an educational tool, so that they see that the cells are centered around the points, and then it piques their curiosity and then they look it up

public bool ShowPoints { get; set; } = false;

[Caption ("Color Sorting")]
public ColorSorting ColorSorting { get; set; } = ColorSorting.Random;
Copy link
Member

Choose a reason for hiding this comment

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

This might need some more thought on how to present it to the user. The effect is neat, but the huge menu of combinations seems intimidating
The menu may change, but I'll also mention that Random Color Sorting could just be Random since it's inside a menu labelled Color Sorting

[Caption ("Reverse Color Sorting")]
public bool ReverseColorSorting { get; set; } = false;

[Caption ("Colors Seed")]
Copy link
Member

Choose a reason for hiding this comment

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

I'd maybe just call this Random Colors since the Seed is mentioned by the "Reseed" button underneath, and similar for the other seed below

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 went ahead and called that Random Colors, and similarly for the points Random Point Locations

shortestDistance = distance;
closestIndex = i;
}
dst_row[x] =
Copy link
Member

Choose a reason for hiding this comment

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

Only using the closest point can produce jagged edges, so you may want to consider doing some sort of blending / antialiasing

Copy link
Contributor Author

@Lehonti Lehonti Jan 15, 2024

Choose a reason for hiding this comment

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

Maybe this would require either changing the algorithm used to make the calculations, or applying some sort edge blur effect (that is not yet developed, as far as I know)?

Copy link
Member

Choose a reason for hiding this comment

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

It could probably be done easily with supersampling, e.g. compute the color at multiple sub-pixel positions and blend - currently the sampling is done at only the upper left corner of the pixel

Most pixels don't need this though since there are large solid regions, so this could be skipped if there isn't another voronoi point close enough to be the closest to any sub pixel location

Copy link
Contributor Author

@Lehonti Lehonti Jan 16, 2024

Choose a reason for hiding this comment

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

Can this be done in a future PR? A change introduced in this PR (the Utility.GeneratePixelOffsets() method) is useful for other effects, too, and there's no risk of breaking an API if we add to this effect in the future.

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 think this is fine to add in a future PR

@Lehonti
Copy link
Contributor Author

Lehonti commented Jan 18, 2024

Now the merge conflicts have been solved

@cameronwhite
Copy link
Member

👍 I think this looks good, the only thing I'm not sure about is the big menu of color sorting options which still feels pretty awkward to me

@Lehonti
Copy link
Contributor Author

Lehonti commented Jan 19, 2024

@cameronwhite what changes do you suggest? I could just reduce them to 3 in each direction (r, g, or b horizontally, and the same vertically). Would that work?

@cameronwhite
Copy link
Member

yeah something like that sounds simpler. I'm mostly just worried about it getting even more complicated if we want to add more options in the future
e.g. it could be useful to also just do an outline with the current palette color instead of filling with random colours

@Lehonti
Copy link
Contributor Author

Lehonti commented Jan 19, 2024

I've now reduced the number of sortings.

As for your last comment, do you mean an outline as in drawing a border around the cells? That is definitely harder than just filling the cells with colors.

It's possible that you mean filling the cells with the colors currently in the palette, and that could be done, it's just that it's very likely that at least a pair (or a group) of adjacent cells will have the same color.


public enum ColorSorting
{
[Caption ("Random Color Sorting")] Random,
Copy link
Member

Choose a reason for hiding this comment

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

Could you change this just to "Random"?
The "color sorting" isn't really necessary since the menu is also called "Color Sorting"

{
[Caption ("Random Color Sorting")] Random,

// Translators: Horizontal color sorting with B as the leading term
Copy link
Member

Choose a reason for hiding this comment

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

For this group of comments, could you change phrases like B to something like blue (B)?
Translators might not be fully aware of the context of what the individual R/G/B characters mean here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No problem, it has been done now

@cameronwhite
Copy link
Member

As for your last comment, do you mean an outline as in drawing a border around the cells? That is definitely harder than just filling the cells with colors.

That is what I meant, but I agree it would be more work to implement and isn't in the scope of this PR

@cameronwhite
Copy link
Member

Looks good, thanks for working on this!

@cameronwhite cameronwhite merged commit f34897c into PintaProject:master Jan 21, 2024
5 checks passed
@Lehonti Lehonti deleted the feature/voronoi branch January 21, 2024 03:59
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.

2 participants