SonarSonic / DrawingBotV3

DrawingBotV3 is a software for converting images into vector art
https://docs.drawingbotv3.com
GNU General Public License v3.0
326 stars 42 forks source link

Fix for Line Sample rounding #116

Closed HanzPetrov closed 6 months ago

HanzPetrov commented 6 months ago

Hi Ollie,

This is a fix for a "rounding error", which can be noticeable when lines should be exactly 0 or 90 degrees, but come out slightly skewed. To test, in PFMSketchLines, set Start Angle Min and Start Angle Max to zero and Angle Tests to 4. All lines should be perpendicular, but some are slightly off. Replacing Math.ceil() with Math.round()fixes this. Another option would be to let the cast to (int) floor the angle instead...

/Hanz

HanzPetrov commented 6 months ago

So, here is a further improved version, now using AffineTransform. Turns out that the docs describe how AffineTransform is designed for exactly this type of application (and the nature of the problem): https://docs.oracle.com/javase/8/docs/api/java/awt/geom/AffineTransform.html Since 90 degrees is represented as PI/2 in radians, and since PI is a transcendental (and therefore irrational) number, it is not possible to exactly represent a multiple of 90 degrees as an exact double precision value measured in radians. As a result it is theoretically impossible to describe quadrant rotations (90, 180, 270 or 360 degrees) using these values. Double precision floating point values can get very close to non-zero multiples of PI/2 but never close enough for the sine or cosine to be exactly 0.0, 1.0 or -1.0. The implementations of Math.sin() and Math.cos() correspondingly never return 0.0 for any case other than Math.sin(0.0).

Performance seems to be on par with the current method. /Hanz

PS. I will lobby separately for the removal of the + 0.5F in nextPathFindingResult() ;p float startAngle = tools.randomFloat(startAngleMin, startAngleMax) + 0.5F; // why add half a degree here?

SonarSonic commented 6 months ago

Thank you Hanz! This is a tricky one, as on paper this is a great change and makes complete sense however in practice the intentional/unintentional inaccuracy produces more subjectively pleasing results / more organic results.

I think your mention of removing the 0.5F is tied to this too, I originally added that 0.5F to avoid the effect you're after, as if lines are generated exactly in line with the pixels it tends to end up creating artifacts which look more digital and less drawn. So while I think your version is more accurate there is something about the intended inaccuracy (in the + 0.5F) and slightly unintended inaccuracy of the Math.ceil. Which actually result in more pleasing results in this particular case on the images I have tested.

What I'm not sure of yet is if we should be using this more accurate version and avoid the artifacts some other way as using "unlimited tests" without the 0.5F can also create some straight-line artifacts.

As you can see from the comparison below with the values you suggest (Start Angle Min/Max = 0, Angle Tests = 4), before the PR the distribution of tones is better thanks to this inaccuracy and the result is more organic, compared to after where while the lines are straight, you can see where the PFM gets stuck "following the pixels" and creating dark patches and it has a more digital appearance, especially on the right-hand side and top of the drawing.

Before PR

rounding_before_v1

After PR

rounding_after_v1

Here's another comparison with default settings before and after, keeping the 0.5F again where the ceil produces more pleasing results with no artefacts. I've highlighted the "digital" artefacts which appear after the PR.

Before PR (Math.ceil)

digital_artifact_ceil

After PR

digital_artifact_round_highlights

SonarSonic commented 6 months ago

Fixed in v1.6.10