hernanat / dcolor

Python Based Domain Coloring
34 stars 9 forks source link

rewrite / restructure / push towards better maintainability #5

Open hernanat opened 7 months ago

hernanat commented 7 months ago

I have to say, I never really thought that this project would be used outside of my own work years and years ago.

Back then, it was just a scrappy little python module that I wrote in part to support part of my senior readings & research work in college.

Over the years, I've received several emails, tweets, some folks have opened PRs, and apparently a group of students used it for their own research.

So, thanks to everyone that have been interested!

That being said, I haven't been satisfied with the state of the library in terms of software quality & maintainability. I would like to rewrite most / all of it with the following goals:

I am not a professional python programmer -- outside of this & a steganography project I've actually largely avoided it. If any folks read this that are well versed in Python & the ecosystem and have any ideas, please share them!

Open to any other ideas folks that use this would like to see, share them here and let's discuss :)

queue-miscreant commented 1 month ago

This is a good place to put my thoughts as any. Regarding the bullet points above:

maintainability / readability: right now it's a mess of things thrown into a single file per strategy

Agreed. There is heavy code reuse between all three coloring variants, and creating new variants will result in further duplication. Fortunately, they might be addressed by a few ideas I have regarding the next bullet point.

I added black as a developer tool in #6, since I like the formatting it emits. I also think adding type hints where possible would be beneficial, since it helps a lot with fitting the pieces of a program together.

usability: make it easier to use inside existing projects (has anyone tried this? I imagine it's painful)

Agreed on this being painful. In my opinion, the way the library is used is at odds with how plotting functions in Matplotlib are used. For example,

bounds = ...
thing_to_plot = ...  # a complex function
figure_arguments = ...
# Plot
DColor(**bounds).plot(thing_to_plot, **figure_arguments)

Is quite different from:

figure_arguments = ...
plot_arguments = ...
thing_to_plot = ...

plt.figure(**figure_arguments)
plt.plot(thing_to_plot, **plot_arguments)
# plt.show()

Sympy is a library which follows a more Matplotlib-like style when it comes to plotting with it. That is, plotting parameters are exposed on the function doing the plotting while fields on the Figure are configured beforehand.

It would also be a good idea to take advantage of Matplotlib features such as colormaps. I have a couple of example images I made on a local branch. Don't take everything there as gospel, since I'm still trying to convince myself of the best way to do things.

This image was made by mapping argument to HSV, mapping magnitudes* to grayscale and blending the two together: generic

*This is a naive implementation using 2arctan(t)/pi to get magnitude into the range [0, 1).

This image was made by mapping argument to a constant value and mapping magnitude* to "flag": magnitude_flag

*Same caveat

The first image gets washed out pretty quickly, but there's no reason the map can't be configurable (and for what it's worth, I think I prefer arctan(ln(t))/pi + 1/2 ). Matplotlib uses Colormap Normalization to translate actual data values to colormap values, which may be beneficial for creating legends.

In summation, the way I recommend exposing the main plotting function would be:

# plt.figure(**figure_arguments)
dcolor.dcolor(thing_to_plot, xlim=..., ylim=..., norm=..., cmap=...)  # returns DColor handle
# plt.show()

Not that the cmap arg there CANNOT be the same type as the typical Matplotlib one, since we're dealing with inherently 2D data

tested: there are zero tests written for this, would like to change that

This is an issue that I'm not sure it's a good idea to resolve until focusing on usability. The most naive way to test things would be to compare new outputs with reference images, but this is a bad idea when it comes to adding more coloring maps.