View Issue Details

IDProjectCategoryView StatusLast Update
0001404NoesisGUIUnitypublic2022-05-08 21:25
Reporternokola Assigned Tojsantos  
PrioritynormalSeveritymajor 
Status resolvedResolutionfixed 
Product Version2.1.0f1 
Target Version3.2.0Fixed in Version3.2.0 
Summary0001404: Cannot draw Path to meet two Arc-s: rendering artifacts (WPF OK)
Description

I wanted to make a color picker, that's basically an ellipse with the top half filled in one color and the bottom filled with another.
To do that, I make the ellipse out of two Paths with Arc for top and bottom (one is clockwise, the other counter-clockwise).

Works great in WPF/Visual Studio, however the same code in Noesis causes drawing artifacts - gaps - between the two arcs.

Steps To Reproduce
  1. Load this in WPF and Noesis:
    <Path x:Name="pathTop" Data="M-80,0 A80,80 0 0 1 80,0" IsHitTestVisible="False" Tag="HT" StrokeThickness="20">
    <Path.Stroke>
    <RadialGradientBrush x:Name="pathTopBrush" GradientOrigin="0.5,1" Center="0.5,1" RadiusX="0.5" RadiusY="1">
    <GradientStop Color="Red"/>
    </RadialGradientBrush>
    </Path.Stroke>
    </Path>
    <Path x:Name="pathBottom" Data="M-80,0 A80,80 0 0 0 80,0" IsHitTestVisible="False" Tag="HT" StrokeThickness="20">
    <Path.Stroke>
    <RadialGradientBrush x:Name="pathBottomBrush" GradientOrigin="0.5,0" Center="0.5,0" RadiusX="0.5" RadiusY="1">
    <GradientStop Color="Green"/>
    </RadialGradientBrush>
    </Path.Stroke>
    </Path>

Expected:
All OK
Actual:
gap between red and green arcs
Added screenshot

Attached Files
PlatformAny

Activities

nokola

nokola

2019-02-05 05:43

reporter   ~0005431

Added attachment

ArcPaintIssue.png (31,486 bytes)   
ArcPaintIssue.png (31,486 bytes)   
sfernandez

sfernandez

2019-02-05 11:21

manager   ~0005432

Last edited: 2019-02-05 11:23

While we fix that problem with the generated stroke, maybe you can use a filled path like this:


<Canvas HorizontalAlignment="Center" VerticalAlignment="Center">
<Path Data="M90.000001,0 C139.70563,0 180,40.294373 180,90 L160,90
C160,51.340069 128.65993,20 90.000001,19.999998 51.34007,20 20.000001,51.340069 19.999997,90
L0,90 C7.4505806E-07,40.294373 40.294373,0 90.000001,0 z"
Fill="Red" Height="90" Canvas.Left="-90" Stretch="Fill" Canvas.Top="-90" Width="180"/>
<Path Data="M0,0 L19.999997,0 C20.000001,38.659927 51.34007,70 90.000001,70 128.65993,70 160,38.659927 160,0
L180,0 C180,49.705627 139.70563,90 90.000001,90 40.294373,90 7.4505806E-07,49.705627 0,0 z"
Fill="Green" Height="90" Canvas.Left="-90" Stretch="Fill" Width="180"/>
</Canvas>

nokola

nokola

2019-02-05 17:32

reporter   ~0005433

Thanks!
The issue is, I also want anti-aliasing. I ended up "simulating" AA by using linear gradient in code. Took me few days (would be nice if it "just works" like in WPF"
Here's what I ended up with that looks proper:

    <Ellipse x:Name="ellipsePickerColor" IsHitTestVisible="False" Tag="HT">
        <Ellipse.Stroke>
            <LinearGradientBrush x:Name="ellipsePickerColorBrush" EndPoint="0.5,1" StartPoint="0.5,0" />
        </Ellipse.Stroke>
    </Ellipse>

    <Ellipse x:Name="ellipsePicker" IsHitTestVisible="False" Tag="HT">
        <Ellipse.Stroke>
            <RadialGradientBrush x:Name="pickerColor"/>
        </Ellipse.Stroke>
    </Ellipse>

Code:
float totalWidth = 160f;
float colorWidth = 18f;
float borderWidth = 2f;
SetupColorPicker(ellipsePicker, pickerColor,
ellipsePickerColor, ellipsePickerColorBrush,
totalWidth, colorWidth, borderWidth, Globals.AntiAliasSize);

    /// <summary>
    /// Creates a color picker with anti-aliased borders from an ellipse and radialgradientbrush attached to it.
    /// </summary>
    /// <param name="picker">ellipse to init color picker at</param>
    /// <param name="brush">radial brush attached to ellipse</param>
    /// <param name="pickerColorUnderneath"></param>
    /// <param name="pickerColorUnderBrush"></param>
    /// <param name="totalWidthEpx">total width of color picker</param>
    /// <param name="colorWidthEpx">width of color part</param>
    /// <param name="borderWidthEpx">width of two borders around color</param>
    /// <param name="aaSize">anti-alias area width</param>
    private static void SetupColorPicker(Ellipse picker,
        RadialGradientBrush brush,
        Ellipse pickerColorUnderneath,
        LinearGradientBrush pickerColorUnderBrush,
        float totalWidthEpx,
        float colorWidthEpx,
        float borderWidthEpx,
        float aaSize)
    {
        borderWidthEpx -= aaSize; // remove half the AA (not aaSize * 2) because aa is assumed to add width only through half of itself
        if (borderWidthEpx < 0)
        {
            borderWidthEpx = 0;
        }
        float radius = totalWidthEpx / 2;
        brush.GradientStops = new GradientStopCollection();
        GradientStopCollection stops = brush.GradientStops;
        float colorStartEpx = aaSize + borderWidthEpx + aaSize + colorWidthEpx;
        picker.Width = totalWidthEpx;
        picker.Height = totalWidthEpx;
        picker.RenderTransform = new TranslateTransform(-radius, -radius);
        picker.StrokeThickness = (colorWidthEpx + borderWidthEpx * 2 + aaSize * 4) + 1;

        stops.Add(new GradientStop { Offset = 1f - (colorStartEpx + aaSize + borderWidthEpx + aaSize + aaSize) / radius }); // AA
        stops.Add(new GradientStop { Offset = 1f - (colorStartEpx + aaSize + borderWidthEpx + aaSize) / radius, Color = Colors.White }); // border
        stops.Add(new GradientStop { Offset = 1f - (colorStartEpx + aaSize + aaSize) / radius, Color = Colors.White }); // AA
        stops.Add(new GradientStop { Offset = 1f - (colorStartEpx + aaSize) / radius, Color = Color.FromRgb(40, 40, 40) }); // dark border
        stops.Add(new GradientStop { Offset = 1f - colorStartEpx / radius }); // picked color

        stops.Add(new GradientStop { Offset = 1f - (aaSize + borderWidthEpx + aaSize + aaSize) / radius }); // picked color + AA
        stops.Add(new GradientStop { Offset = 1f - (aaSize + borderWidthEpx + aaSize) / radius, Color = Color.FromRgb(40, 40, 40) }); // dark border
        stops.Add(new GradientStop { Offset = 1f - (aaSize + borderWidthEpx) / radius, Color = Colors.White }); // border
        stops.Add(new GradientStop { Offset = 1f - aaSize / radius, Color = Colors.White }); // AA
        stops.Add(new GradientStop { Offset = 1f });

        // setup lower ellipse - a linear gradient used to effectively draw 2 arcs
        // needed due to Noesis issue:
        // "Cannot draw Path to meet two Arc-s: rendering artifacts (WPF OK)" https://www.noesisengine.com/bugs/view.php?id=1404
        // At the end lower ellipse will look like:
        // <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
        //     <GradientStop Color="Red" Offset="0"/>
        //     <GradientStop Color="Red" Offset="0.5"/>
        //     <GradientStop Color="Green" Offset="0.5"/>
        //     <GradientStop Color="Green" Offset="1"/>
        // </LinearGradientBrush>
        pickerColorUnderneath.StrokeThickness = colorWidthEpx + aaSize * 2;
        float thicknessDelta = picker.StrokeThickness - pickerColorUnderneath.StrokeThickness;
        float colorEllipseSize = totalWidthEpx - thicknessDelta; // this will center stroke  
        pickerColorUnderneath.Width = colorEllipseSize;
        pickerColorUnderneath.Height = colorEllipseSize;
        pickerColorUnderneath.RenderTransform = new TranslateTransform(-radius, -radius);

        pickerColorUnderBrush.GradientStops = new GradientStopCollection();
        GradientStopCollection underStops = pickerColorUnderBrush.GradientStops;
        underStops.Add(new GradientStop() { Offset = 0 });
        underStops.Add(new GradientStop() { Offset = 0.5f });
        underStops.Add(new GradientStop() { Offset = 0.5f });
        underStops.Add(new GradientStop() { Offset = 1 });
    }

A lot of code to work around AA and the above issue. Pasting it here for information to hopefully influence further Noesis improvements and help others who may be facing similar issue.
Again thank you for the fast response!

jsantos

jsantos

2022-05-08 21:25

manager   ~0007919

Arc gaps has been fixed.

Regarding better AA, our algorithm is optimized for speed vs quality. You have two options to improve this:

  1. Use GPU support enabling MSAA
  2. User our cheap PPAA algorith and tweak it for specific cases

More information available at https://www.noesisengine.com/docs/Gui.Core.AntialiasingTutorial.html

Thanks for your feedback!

Issue History

Date Modified Username Field Change
2019-02-05 05:41 nokola New Issue
2019-02-05 05:43 nokola File Added: ArcPaintIssue.png
2019-02-05 05:43 nokola Note Added: 0005431
2019-02-05 10:46 sfernandez Assigned To => jsantos
2019-02-05 10:46 sfernandez Status new => assigned
2019-02-05 11:21 sfernandez Note Added: 0005432
2019-02-05 11:23 sfernandez Note Edited: 0005432
2019-02-05 17:32 nokola Note Added: 0005433
2022-03-07 19:49 jsantos Target Version => 3.2.0
2022-05-08 21:25 jsantos Status assigned => resolved
2022-05-08 21:25 jsantos Resolution open => fixed
2022-05-08 21:25 jsantos Fixed in Version => 3.2.0
2022-05-08 21:25 jsantos Note Added: 0007919
2025-10-10 13:29 jsantos Category Unity3D => Unity