Oleg 的个人资料Oleg V. Polikarpotchkin ...日志网络 工具 帮助

日志


6月14日

ColorChooser Control

This element is the one more control from the ColorPicker constituent controls bug in addition to the published in my recent posts.

The ColorChooser control is the User Control containing the KnownColorPicker and RainbowColorPicker as alternative ways to pick the color and the OpacitySlider to set its opacity. It resembles the Windows Common Color Dialog which we are accustomed to.

That is the sample.

ColorChooser

The ColorChooser has the SelectedColor Dependency Property used to get or set the value of the Color selected. When the SelectedColor changes the ColorChooser raises the SelectedColorChanged event.

On the base of the ColorChooser control the ColorDialog dialog is built .

The code

You can download the code and the samples here:


The archive contains my WPFGears library with samples, tests, etc. It’s the Visual Studio 2008 SP1 solution targeted to .NET Framework 3.5.

You’ll find ColorChooserSample and ColorDialogSample projects there. The ColorChooser control and the ColorDialog  dialog code is in the WPFGears library project ColorPickerControls folder.

Regards,
Oleg V. Polikarpotchkin

Technorati Теги: ,,

AllInOneColorChooser Control

This element is the one more control from the ColorPicker constituent controls bug in addition to the published in my recent posts.

The AllInOneColorChooser control is the User Control composed of all the components which were developed as the parts of the ColorPicker controls bag. It provides you with the four different color pickers, and you can choose the one you are filling the most comfortable with. In addition it allows to set the color opacity with the opacity slider.

The pictures below show you different color pickers in action.

AllInOneColorChooser1 AllInOneColorChooser2
AllInOneColorChooser3 AllInOneColorChooser4

Sincerely I don't think that it would be very good idea to use AllInOneColorChooser in the production code. Rather you could regard it as the toy allowing you to try and compare different possibilities in one place and then choose one you prefer.

The AllInOneColorChooser has the SelectedColor Dependency Property used to get or set the value of the Color selected. When the SelectedColor changes the AllInOneColorChooser raises the SelectedColorChanged event.

The code

You can download the code and the samples here:


The archive contains my WPFGears library with samples, tests, etc. It’s the Visual Studio 2008 SP1 solution targeted to .NET Framework 3.5.

You’ll find AllInOneColorChooserSample project there. AllInOneColorChooser control code is in the WPFGears library project ColorPickerControls folder.

Regards,
Oleg V. Polikarpotchkin

Technorati Теги: ,,

KnownColorPicker Control

This element is the one more control from the ColorPicker constituent controls bug in addition to the published in my recent posts.

The KnownColorPicker control is designed as the tool to select the Color from the set of predefined colors. It's the very simple User Control and is developed as the separate entity to encapsulate the predefined color sets initialization code and ListBox and ComboBox controls templates.

The KnownColorPicker control has two sets of predefined colors. The first one contains 64 colors which we can see in the Windows Common Color Dialog. This set is labeled as "Base Colors" and is presented in the ListBox at the top. The second one contains all the colors defined in the Windows.System.Windows.Media.Colors class. This set is labeled as "All WellKnown Colors" and is presented in the ComboBox at the bottom.

That is the sample:

KnownColorPicker

At the figure above the Bisque border is given to the KnownColorPicker control just to make it distinctive.

The KnownColorPicker has the SelectedColor Dependency Property used to get or set the value of the Color selected. When the SelectedColor changes the KnownColorPicker raises the SelectedColorChanged event.

The code

You can download the code and the samples here:


The archive contains my WPFGears library with samples, tests, etc. It’s the Visual Studio 2008 SP1 solution targeted to .NET Framework 3.5.

You’ll find KnownColorPickerSample project there. KnownColorPicker control code is in the WPFGears library project ColorPickerControls folder.

Regards,
Oleg V. Polikarpotchkin

Technorati Теги: ,,
6月11日

RainbowColorPicker2 Control

The RainbowColorPicker2 control is the composition of the RainbowSlider and the ColorSaturationBox controls. The RainbowColorPicker2 is full fledged color picker allowing you to select the color Hue with its RainbowSlider and the color Saturation and Luminosity with the ColorSaturationBox part.

That is the sample:

RainbowColorPicker2

At the figure above the Bisque border is given to the RainbowColorPicker2 control just to make it distinctive.

The RainbowColorPicker2 has the SelectedColor Dependency Property used to get or set the value of the Color selected. When the SelectedColor changes the RainbowColorPicker2 raises the SelectedColorChanged event.

The code

You can download the code and the samples here:


The archive contains my WPFGears library with samples, tests, etc. It’s the Visual Studio 2008 SP1 solution targeted to .NET Framework 3.5.

You’ll find RainbowColorPicker2Sample project there. RainbowColorPicker2 control code is in the WPFGears library project ColorPickerControls folder.

Regards,
Oleg V. Polikarpotchkin

Technorati Теги: ,,

RainbowColorPicker Control

This element is the one more control from the ColorPicker constituent controls bug in addition to the published in my recent posts.

The RainbowColorPicker control is the composition of the RainbowBox and the LuminositySlider controls. The RainbowColorPicker is full fledged color picker allowing you to select the color Hue and Saturation with its RainbowBox and the color Luminosity with the LuminositySlider part.

That is the sample:

RainbowColorPicker

At the figure above the Bisque border is given to the RainbowColorPicker control just to make it distinctive.

The RainbowColorPicker has the SelectedColor Dependency Property used to get or set the value of the Color selected. When the SelectedColor changes the RainbowColorPicker raises the SelectedColorChanged event.

The code

You can download the code and the samples here:


The archive contains my WPFGears library with samples, tests, etc. It’s the Visual Studio 2008 SP1 solution targeted to .NET Framework 3.5.

You’ll find RainbowColorPickerSample project there. RainbowColorPicker control code is in the WPFGears library project ColorPickerControls folder.

Regards,
Oleg V. Polikarpotchkin

Technorati Теги: ,,
6月10日

LuminositySlider Control

This element is one more control from the ColorPicker constituent controls bug in addition to the published in my recent posts.

LuminositySlider control is designed as the selector of the Color with the HSL Color model Luminosity ranging from 0 to 1. It's the extension of the Slider control.

LuminositySlider is provided with the reference Color through the BaseColor Dependency Property. It displays that reference Color gradient starting from the reference Color with the Luminosity set to 0 up to the Color with the Luminosity of 1.

That is the sample:

LuminositySlider

At the top is the RainbowSlider allowing to select the BaseColor for two LuminositySliders (horizontal and vertical) below it.

LuminositySlider has read-only SelectedColor Dependency Property used to get the value of the Color selected. This property value is synchronized with Minimum, Maximum and Value properties values of the parent Slider control. The caller can set the desired luminance value from the code as follows:

Value = Luminance * (Maximum - Minimum) + Minimum;

When the SelectedColor changes the LuminositySlider raises the SelectedColorChanged event.

The direction the Luminosity value changes depends on the value of IsDirectionReversed property. If it's false (the default) the minimum Luminosity is at the left or at the bottom of the control depending on the orientation.

The code

You can download the code and the samples here:


The archive contains my WPFGears library with samples, tests, etc. It’s the Visual Studio 2008 SP1 solution targeted to .NET Framework 3.5.

You’ll find LuminositySliderSample project there. LuminositySlider element code is in the WPFGears library project ColorPickerControls folder.

Regards,
Oleg V. Polikarpotchkin

Technorati Теги: ,,,
6月9日

RainbowBox Element

This element is the one more control from the ColorPicker constituent controls bug in addition to the published in my recent posts.

RainbowBox element is designed as the tool to select a Color from the color spectrum and is close to the one in the Windows Common Color dialog. It extends the FrameworkElement.

The horizontal color spectrum is overlaid by the vertical gradient ranging from transparent color to Gray color. That is the sample.

RainbowBox

You select the color with the color selector (small white circle with black border). You can drag it with the mouse or just click anywhere on the RainbowBox element surface. You can provide you own color selector drawing with the RainbowBox.Selector Dependency Property.

Read-only SelectedColor Dependency Property of the RainbowBox used to get the value of the Color selected. User can change the SelectedColor property from its code using the SelectorPosition property. The SelectorPosition property gets or sets the selector position normalized to [0,1 0,1] rectangle. Another way to change the SelectedColor property from the code is the RainbowBox.TrySetSelectedColor method. It gets the color argument and tries to find that color in the Rainbow. On success it sets the selector position accordingly and returns true; otherwise it returns false. When the SelectedColor changes the RainbowBox raises the SelectedColorChanged event.

The Rainbow Dependency Property allows to get or set the color spectrum. It's of the GradientStopCollection type i.e. represents the sequence of colors along with the position of the color in the spectrum. By default the spectrum is the sequence of equally spaced Red, Orange, Yellow, Green, Blue, Indigo, Violet, Red colors. When the Rainbow property changes the RainbowBox tries to find the current SelectedColor value in the new Rainbow. On success it sets the selector position accordingly; otherwise it sets the selector position to the top-left corner and changes its SelectedColor to the value corresponding to that position.

The code

You can download the code and the samples here:


The archive contains my WPFGears library with samples, tests, etc. It’s the Visual Studio 2008 SP1 solution targeted to .NET Framework 3.5.

You’ll find RainbowBoxSample project there. RainbowBox element code is in the WPFGears library project ColorPickerControls folder.

Regards,
Oleg V. Polikarpotchkin

Technorati Теги: ,,,
6月8日

ColorSaturationBox Element

This element is one more control from the ColorPicker constituent controls bug in addition to ones published in my recent posts.

ColorSaturationBox element is designed as the tool to select a Color from the mix of some Color with the White and Black colors. It extends the FrameworkElement. The idea of this element is borrowed from the Uncommon Dialogs: Font Chooser & Color Picker Dialogs article.

The color surface to select color from is the superposition of some color gradients. Roughly it consists of

  1. Vertical BaseColor-Black gradient.
  2. Vertical White-Black gradient with Horizontal Opacity mask gradient.

BaseColor is the variable reference color you can get or set with the BaseColor Dependency Property.

That is the sample.

ColorSaturationBox

The ColorSaturationBox element is at the top-left window corner. At right of it is the RainbowSlider (see here) used to select the value of BaseColor. The SelectedColor sample is at the right-top corner. Below it the SelectedColor components are displayed in numbers.

You select the color with the color selector (small white circle with black border). You can drag it with the mouse or just click anywhere on the ColorSaturationBox element surface. A propos, you can provide you own color selector drawing with the ColorSaturationBox.Selector Dependency Property.

Read-only ColorSaturationBox.SelectedColor Dependency Property used to get the value of the Color selected. User can change the SelectedColor property from its code using the SelectorPosition property. The SelectorPosition property gets or sets the selector position normalized to [0,1 0,1] rectangle. When the SelectedColor changes the ColorSaturationBox raises the SelectedColorChanged event.

The code

You can download the code and the samples here:


The archive contains my WPFGears library with samples, tests, etc. It’s the Visual Studio 2008 SP1 solution targeted to .NET Framework 3.5.

You’ll find ColorSaturationSample project there. ColorSaturationBox element code is in the WPFGears library project ColorPickerControls folder.

Regards,
Oleg V. Polikarpotchkin

Technorati Теги: ,,
6月7日

RainbowSlider Control

Yesterday I’ve posted the article on the OpacitySlider Control. The RainbowSlider is one else control to the bug of Color Picker related components.

RainbowSlider control is designed as the mean to select a Color from the color spectrum given. It's the extension of the Slider control.

RainbowSlider control overrides the default template of the parent Slider control and looks like follows:

RainbowSlider

i.e. its style redefines the slider background and the constituent Thumb control template.

As you can see at the figure above the RainbowSlider control takes into account the parent Slider control Orientation property and can be presented as the horizontal or the vertical bar.

Read-only SelectedColor Dependency Property of the RainbowSlider used to get the value of the Color selected. This property value is synchronized with Minimum, Maximum and Value properties values of the parent Slider control. To set the SelectedColor value the caller can set the Value property value to the position of the color in the spectrum (linear interpolation over the Rainbow color sequence involved). Another way to change the SelectedColor property from the code is the RainbowSlider.TrySetSelectedColor method. It gets the color argument and tries to find that color in the Rainbow. On success it sets the selector position accordingly and returns true; otherwise it returns false. When the SelectedColor changes the RainbowSlider raises the SelectedColorChanged event.

The Rainbow Dependency Property allows to get or set the color spectrum. It's of the GradientStopCollection type i.e. represents the sequence of colors along with the position of the color in the spectrum. By default the spectrum is the sequence of equally spaced Red, Orange, Yellow, Lime, Blue, Indigo, Violet, Red colors. When the Rainbow property changes the RainbowSlider tries to find the current SelectedColor value in the new Rainbow. On success it sets the selector position accordingly; otherwise it sets the selector position to the minimum and changes its SelectedColor to the value corresponding to that position.

The direction the SelectedColor value changes depends on the value of IsDirectionReversed property. If it's false (the default) the initial spectrum color is at the left or at the bottom of the control depending on the orientation.

The code

You can download the code and the samples here:


The archive contains my WPFGears library with samples, tests, etc. It’s the Visual Studio 2008 SP1 solution targeted to .NET Framework 3.5.

You’ll find RainbowSliderSample project there. RainbowSlider element code is in the WPFGears library project ColorPickerControls folder.


Regards,
Oleg V. Polikarpotchkin

Technorati Теги: ,,,
6月6日

OpacitySlider Control

OpacitySlider control is designed as the Opacity value selector. It's the extension of the Slider control.

Read-write SelectedOpacity Dependency Property of the OpacitySlider control allows to get or set the value of Opacity. It's of the double type and its value is in the [0,1] range (0 means transparent, 1 - opaque, the default).

OpacitySlider SelectedOpacity property value is synchronized with Minimum, Maximum and Value properties values of the parent Slider control. To get or set the Opacity value the caller can use both OpacitySlider and Value properties taking into account the simple relation:

SelectedOpacity = 1.0 - (Value - Minimum) / (Maximum - Minimum);

OpacitySlider control overrides the default template of the parent Slider control and looks like follows:

OpacitySlider

i.e. its style redefines the slider background and the constituent Thumb control template.

As you can see at the figure above the OpacitySlider control takes into account the parent Slider control Orientation property and can be presented as the horizontal or the vertical bar.

The direction the Opacity value changes depends on the value of IsDirectionReversed property. If it's false (the default) the maximum opacity as at the left or at the bottom of the control depending on the orientation.

The code

You can download the code and the samples here:


The archive contains my WPFGears library with samples, tests, etc. It’s the Visual Studio 2008 SP1 solution targeted to .NET Framework 3.5.

You’ll find OpacitySliderSample project there.OpacitySlider control code is in the WPFGears library project ColorPickerControls folder.


Regards,
Oleg V. Polikarpotchkin

Technorati Теги: ,,,,
6月4日

Math Value Converters

Yesterday I’ve posted the topic on Math Expression Interpreter. It’s handy to use for Math calculations with WPF Value Converters. See the code below.

/// <summary>
/// Converts the input value to other value using the string
/// parameter value as the math expression.
/// </summary>
public class MathConverter : IValueConverter
{
    #region IValueConverter Members
    /// <summary>
    /// Converts the input <paramref name="value"/> to other value using the string
    /// <paramref name="parameter"/> value as the math expression.
    /// </summary>
    /// <param name="value">The value produced by the binding source.</param>
    /// <param name="targetType">Output value type.</param>
    /// <param name="parameter">Math expression string.</param>
    /// <param name="culture">The culture used to convert to/form double values.</param>
    /// <returns>Either the value calculated by the expression given in <paramref name="parameter"/>
    /// or <see cref="DependencyProperty.UnsetValue"/>.</returns>
    public object Convert(object value, Type targetType, object parameter
        , CultureInfo culture)
    {
        if (value == null || parameter == null || !(parameter is string))
            return DependencyProperty.UnsetValue;
        try
        {
            Expression interpreter = new Expression(parameter as string);

            // Expression parameter.
            double expressionParameter;
            if (value is string)
                expressionParameter = double.Parse(value as string, culture.NumberFormat);
            else
                expressionParameter = (double)System.Convert.ChangeType(value, typeof(double)
                    , culture.NumberFormat);
            
            double result = interpreter.Evaluate(new double[] { expressionParameter });
            
            // The result to the target type
            if (double.IsNaN(result) || double.IsInfinity(result))
            {
                if (targetType == typeof(double))
                    return result;
                else if (targetType == typeof(float))
                {
                    if (double.IsNaN(result))
                        return float.NaN;
                    else if (double.IsPositiveInfinity(result))
                        return float.PositiveInfinity;
                    else if (double.IsNegativeInfinity(result))
                        return float.NegativeInfinity;
                }

                return DependencyProperty.UnsetValue;
            }

            return System.Convert.ChangeType(result, targetType, culture.NumberFormat);
        }
        catch (Exception)
        {
            return DependencyProperty.UnsetValue;
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter
        , CultureInfo culture)
    {
        throw new NotImplementedException();
    }
    #endregion IValueConverter Members
}

/// <summary>
/// Converts the input array of values to other value using the string
/// parameter value as the math expression.
/// </summary>
public class MathMultiConverter : IMultiValueConverter
{
    #region IMultiValueConverter Members
    /// <summary>
    /// Converts the input <paramref name="values"/> to other value using the string
    /// <paramref name="parameter"/> value as the math expression.
    /// </summary>
    /// <param name="values">The value array produced by the binding source.</param>
    /// <param name="targetType">Output value type.</param>
    /// <param name="parameter">Math expression string.</param>
    /// <param name="culture">The culture used to convert to/form double values.</param>
    /// <returns>Either the value calculated by the expression given in <paramref name="parameter"/>
    /// or <see cref="DependencyProperty.UnsetValue"/>.</returns>
    public object Convert(object[] values, Type targetType, object parameter
        , CultureInfo culture)
    {
        if (values == null || parameter == null || !(parameter is string))
            return DependencyProperty.UnsetValue;
        try
        {
            Expression interpreter = new Expression(parameter as string);

            // Expression parameters array.
            double[] expressionParameters = new double[values.Length];
            for (int i = 0; i < values.Length; i++)
            {
                if (values[i] is string)
                    expressionParameters[i] = double.Parse(values[i] as string, culture.NumberFormat);
                else
                    expressionParameters[i] = (double)System.Convert.ChangeType(values[i]
                        , typeof(double), culture.NumberFormat);
            }
            
            double result = interpreter.Evaluate(expressionParameters);

            // The result to the target type
            if (double.IsNaN(result) || double.IsInfinity(result))
            {
                if (targetType == typeof(double))
                    return result;
                else if (targetType == typeof(float))
                {
                    if (double.IsNaN(result))
                        return float.NaN;
                    else if (double.IsPositiveInfinity(result))
                        return float.PositiveInfinity;
                    else if (double.IsNegativeInfinity(result))
                        return float.NegativeInfinity;
                }

                return DependencyProperty.UnsetValue;
            }
            return System.Convert.ChangeType(result, targetType, culture.NumberFormat);
        }
        catch (Exception)
        {
            return DependencyProperty.UnsetValue;
        }
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter
        , CultureInfo culture)
    {
        throw new NotImplementedException();
    }
    #endregion IMultiValueConverter Members
}

 

The first one is for use with the regular Binding class, and the second is for the MultiBinding.

Regards,
Oleg V. Polikarpotchkin

6月3日

Math Expressions Interpreter

Introduction

I’ve ported from C++ to C# my old expression interpreter. It’s written from scratch without any parser generators like, say, ANTLR. Maybe it isn’t the modern approach but I’m using this code during last twenty years or so.

Basics

An expression contains parameters, numeric and predefined symbolic constants, predefined functions calls.

All elements type is treated as double. Evaluation result is also double (having, possibly, special values NaN, INF, -INF).

Samples:

  • (1+2)*5
  • 2*sqrt(4)
  • if({0}-4,{0}-10,33,{0}+10)

Usage

Expression class constructor takes an expression string as a parameter and parses it into the tree of operations. Exceptions of some types could be thrown during parsing.

Then the expression could be evaluated multiple times with different parameters set with the Evaluate method. Evaluation is always successful (doesn't throws exceptions) although the result could be one of the double type special values mentioned above.

Constants

An expression can contain numeric literals and predefined symbolic constants (like "PI").

The decimal point is used as the separator in literals. Scientific notation 'e' or 'E' can be used.

Predefined Constants

The following symbolic constant names are defined:

  1. Natural logarithm base "e" = 2.71...
  2. "PI" = 3.14...
  3. Not-a-number value "NaN".
  4. Positive infinity "Inf".
  5. Negative infinity "NInf".

Constant names are case-insensitive.

Parameters

Expression parameters (variables) are denoted with the placeholder syntax {index} like in the string.Format method format string. The 'index' is the parameter position in the array passed to the Evaluate method call. If the parameter at the specified index is missed the NaN value used.

Operations

Operations priorities are as follows (the top is highest).

  1. Function calls and parentheses ().
  2. Unary plus (+) and minus (-).
  3. Exponentiation (^).
  4. Multiplication (*), division (/), modulus (%).
  5. Addition (+) and subtraction (-).

Parentheses

Any level parentheses used to group the operations.

Function calls

All functions return double value and can have one or more arguments. Syntax: function(arg1, arg2, …). Argument itself also could be an expression. Some functions can have variable argument count.

Unary plus and minus

Unary plus is ignored. Unary minus is right associative, i.e. --x means (-(-x)). When Unary minus is applied to an infinity its sign changes to the opposite (e.g. Inf becomes NInf and vice versa).

Exponentiation

Pow function analog. The operation is right associative, i.e. x^y^z means (x^(y^z)).

Any values except NaN powered to 0 equal to 1. NaN at any power equal to NaN.

Multiplication, division, modulus

These operations are left associative, i.e. x*y/z means ((x*y)/z).

Addition and subtraction

These operations are left associative, i.e. x+y-z means ((x+y)-z).

Functions

Function names are case-insensitive.

Conditional Functions

Conditional functions allow emulate a branching in the intrinsically linear expression syntax.

First argument value of any conditional function is compared to the branching condition defined by that function. Depending on the comparison result one of the other arguments is calculated.

Branching by sign (If)

If function has 4 or 5 arguments.

If(cond, var1, var2, var3)

or

If(cond, var1, var2, var3, spec)

If function checks the cond argument has a special value. If so, two variants are possible:

  • If the spec argument exists it's calculated.
  • Otherwise the function returns the value of cond, i.e. NaN, Inf or NInf.

If the cond argument doesn't have a special value, If function calculates and returns the value of one of three arguments.

  1. var1 on cond < 0
  2. var2 on cond == 0
  3. var3 on cond > 0

Branching by special value (IfNotFinite)

IfNotFinite function has 2 or 3 arguments.

IfNotFinite(cond, spec)

or

IfNotFinite(cond, spec, var)

If the cond argument has a special value the function calculates and returns the spec argument value. Otherwise, if var argument exists the function calculates and returns it. Else it returns the cond value.

Branching by NaN (IfNaN)

IfNaN function has 2 or 3 arguments.

IfNaN(cond, spec)

or

IfNaN(cond, spec, var)

If the cond argument value is NaN the function calculates and returns the spec argument value. Otherwise, if var argument exists the function calculates and returns it. Else it returns the cond value.

Branching by Infinity (IfInf)

IfInf function has 2 or 3 arguments.

IfInf(cond, spec)

or

IfInf(cond, spec, var)

If the cond argument value is Inf or NInf the function calculates and returns the spec argument value. Otherwise, if var argument exists the function calculates and returns it. Else it returns the cond value.

Branching by Positive Infinity (IfPInf)

IfPInf function has 2 or 3 arguments.

IfPInf(cond, spec)

or

IfPInf(cond, spec, var)

If the cond argument value is Inf the function calculates and returns the spec argument value. Otherwise, if var argument exists the function calculates and returns it. Else it returns the cond value.

Branching by Negative Infinity (IfNInf)

IfNInf function has 2 or 3 arguments.

IfNInf(cond, spec)

or

IfNInf(cond, spec, var)

If the cond argument value is NInf the function calculates and returns the spec argument value. Otherwise, if var argument exists the function calculates and returns it. Else it returns the cond value.

Math Functions

Absolute value Abs

Abs has one argument and returns its absolute value. The same is applied to the negative infinity: Inf is returned in place of NInf.

Square root Sqrt

Sqrt has one argument and returns its square root. The square root of negative number, including negative infinity, and NaN, is NaN. The square root of Inf is Inf.

The Sqrt(x) is the analog of Pow(x, 0.5).

Exponentiation Pow

Pow has two arguments and returns the first argument raised to the power of the second argument. It's the analog of ^ operation.

Any value except NaN at the power of 0 is equal to 1. NaN at any power is equal to NaN.

Logarithms

Log and Log10 functions has one argument. Logarithm of 0 is equal to NInf. Logarithm of negative argument is NaN.

Thrigonometric functions

Sin, Cos and Tan functions has one argument. If the argument has any special value the result will be NaN.

Reverse thrigonometric functions

Asin, Acos and Atan functions has one argument and return the angle value in radians or a special value.

If Asin or Acos argument is outside of [-1,1] or has a special value the result will be NaN.

If Atan argument has a special value the result will be:

  1. NaN for NaN
  2. pi/2 for Inf
  3. -pi/2 for NInf

Hyperbolic functions

Sinh, Cosh and Tanh functions has one argument.

The code

You can download the code here:

 

Regards,
Oleg V. Polikarpotchkin

5月4日

WPF Choose Font Dialog

WPF still doesn’t provide out of the box common Font Chooser dialog, and I can’t realize if Microsoft is going to provide such the dialog in the forthcoming WPF 4.0 version.

Currently we have the following alternatives:

  1. Microsoft Windows SDK provides the WPFNotepad sample where the Sample FontChooser is implemented. This implementation conforms to recommendations in the WPF Font Selection Model whitepaper.
  2. Sample Font Chooser by Niklas Borson.
  3. Pure XAML Font Chooser by Norris Cheng.

The former two items are full-fledged Font Chooser dialog implementations, and the latter one is the sample of how to implement the basic Font Chooser in pure XAML. Maybe there are other implementations but I could not find them.

From full-fledged options I prefer the one of Niklas Borson: it has more compact design and, as much as I tested it, all its features work fine. The only drawback is that the dialog initialization takes too much time.

There are things I don’t like in both the first and the second implementations: they are written rather in the Windows Forms style than in the WPF one. The dialog controls and layout are defined in the XAML. All the logic is implemented in the dialog code behind file, and this file is long and complicated. There is no one Binding object used!

I have rewrote the Font Chooser dialog basing on the approach close to the MVC pattern: there is the Font properties class, the Model, and the dialog controls binds to the properties of this model class in XAML. The code behind file contains as little code as possible. The implementation consists of the following main parts:

  • FontInfo class contains the set of properties describing the Font in the WPF world. FontInfo class plays the role of the Model in the MVC pattern.
  • The dialog contains a lot of controls defined in the ChooseFont.xaml which represent the View. These controls are bound to the Font properties defined in the FontInfo class.
  • ChooseFont.xaml.cs file code provides the constructors, OK button handler and some event handlers implementing the auto complete behavior in the dialog.

FontDialog

You can download the dialog source code here:

Regards,
Oleg V. Polikarpotchkin

Technorati Теги: ,,,
4月25日

Represent an enumeration as a sequence

There is a nice way to represent an arbitrary enumeration as a sequence of its names, values or name/value pairs. To do this we have to use extension methods:

using System;
using System.Linq;
using System.Collections.Generic;
/// <summary>
/// Contains extension method on the <see cref="System.Type"/> allowing to represent
/// an enumeration members as a sequence.
/// </summary>
public static class EnumExtensions
{
    /// <summary>
    /// Get values of the specified enumeration type.
    /// </summary>
    /// <param name="type">Extendee type.</param>
    /// <returns>The sequence of values of the enumeration specified.</returns>
    public static IEnumerable<object> Values(this Type type)
    {
        Type enumType = Nullable.GetUnderlyingType(type) ?? type;
        if (!enumType.IsEnum)
            yield break;

        foreach (object item in Enum.GetValues(enumType))
            yield return Convert.ChangeType(item, Enum.GetUnderlyingType(enumType));
    }

    /// <summary>
    /// Get names of the specified enumeration type.
    /// </summary>
    /// <param name="type">Extendee type.</param>
    /// <returns>The sequence of names of the enumeration specified.</returns>
    public static IEnumerable<string> Names(this Type type)
    {
        Type enumType = Nullable.GetUnderlyingType(type) ?? type;
        if (!enumType.IsEnum)
            yield break;

        foreach (string item in Enum.GetNames(enumType))
            yield return item;
    }

    /// <summary>
    /// Get name/value pairs of the specified enumeration type.
    /// </summary>
    /// <param name="type">Extendee type.</param>
    /// <returns>The sequence of name/value pairs of the enumeration specified.</returns>
    public static IEnumerable<KeyValuePair<string, object>> Dictionary(this Type type)
    {
        Type enumType = Nullable.GetUnderlyingType(type) ?? type;
        if (!enumType.IsEnum)
            yield break;

        IEnumerable<KeyValuePair<string, object>> dictionary =
            from value in Enum.GetValues(enumType).Cast<object>()
            select new KeyValuePair<string, object>(Enum.GetName(enumType, value)
                , Convert.ChangeType(value, Enum.GetUnderlyingType(enumType)));
        
        foreach (KeyValuePair<string, object> item in dictionary)
            yield return item;
    }
}

This could be useful when, for example, the task is to fill the contents of the ListBox or an another such control with the list of enumeration values.

In WPF, for example, if we have three list boxes which we’re going to fill with the values of System.UriPartial enumeration all we have to do is the following:

Type enumType = typeof(UriPartial);
lbxValues.ItemsSource = enumType.Values();
lbxNames.ItemsSource = enumType.Names();
lbxDictionary.ItemsSource = enumType.Dictionary();

and we'll get the following picture:

Figure1

I’m aware on other approaches to this task (see, for example, Accessing Enum members in Xaml by Andrew Smith or Binding and using Friendly enums in WPF by Sasha Barber), this is just the another one.

Cheers,
Oleg V. Polikarpotchkin

Technorati Теги: ,,,

Generic Data Points Series XML format, Version 2

New version of this service is published yesterday at the CodeProject article. It contains the following additions:

  1. Added support for on-the-fly XML schema generation.
  2. The Loader class interface modified to load Data Points Series XML data with either the default schema, the schema provided by the caller, or the schema generated from the type mappings XML file.
  3. The IXmlConverter interface and its default implementation added.
  4. The DataPointSeries class interface modified to return the results of the Data Points Series XML data parsing as either a collection of raw XsdDataPoint objects or type safe DataPoint objects.

Cheers,
Oleg V. Polikarpotchkin

Technorati Теги: ,,,
4月7日

How to express series of generic data points in XML and read them without a pain

Foreword

It’s often desirable to provide generic data point series data in some XML format. XML gives the ability to decorate the series with attributes, nest them in complex data objects, mix different data series/data objects in one data file and load them with concise LINQ to XML code.

Generic data point is a structure with just two required {X, Y} properties expressing the point position in 2D space. Each point dimension has its own "base" type, e.g. numeric, DateTime, etc, that’s why the term "generic" is applied. The data point series contain one or more data points with matching dimensions base types.

Generic Data Points Series XML format

Data point series XML format has to be developed in a way providing for ability of validation with XML schema.

First, we have to define the root element. Suppose it is called Items. For the sake of safety we’ll require it defines the default XML namespace urn:PointSeries-schema. I.e. the root element looks like that:

<?xml version="1.0" encoding="utf-8"?>
<Items xmlns="urn:PointSeries-schema">
</Items>

 

The root element contains unrestricted number of data point series. First try is to define point series elements as follows:

<Items xmlns="urn:PointSeries-schema">
  <Points …>
  </Points>
  <Points …>
  </Points>
</Items>

 

That won’t work because different point series elements could contain points of different base types and, so, the point series elements themselves could be of different types. XML schema rules don’t allow elements of different types have the same name in the same scope. Hence we must assign different names to point series elements of different types.

It’s decided to name point series elements according to the following patterns:

  • <Points.BaseType …> if both data series dimensions have the same base type. E.g. <Points.Double …>.
  • <Points.XBaseType.YBaseType …> if data series dimensions have the different base types. E.g. <Points.DateTime.Int …>.

BaseType, XBaseType and YBaseType point series element name parts are called collectively "type strings". It's necessary to draw an agreement on how to define these type strings, establish the mapping between the type strings, XSD-defined data types and .NET data types.

Table 1. XSD type to .NET type to type string mapping
XSD Type Description Examples Type string .NET type
xsd:int An integer that can be represented as a four-byte, two’s complement number -2147483648, 2147483645,..., -3, -2, -1, 0, 1, 2, 3, … Int System.Int32
xsd:double IEEE 754 64-bit floating-point number -INF, 1.401E-90, -1E4, -0, 0, 12.78E-2, 12, INF, NaN, 3.4E42 Double System.Double
xsd:dateTime A particular moment in Coordinated Universal Time, up to an arbitrarily small fraction of a second 1999-05-31T13:20:00.000-05:00, 1999-05-31T18:20:00.000Z, 1999-05-31T13:20:00.000, 1999-05-31T13:20:00.000-05:32 DateTime System.DateTime
xsd:date A specific day in history 0044-03-15, 0001-01-01, 1969-06-27, 2000-10-31, 2001-11-17 Date System.DateTime
xsd:gMonth A month in no particular year --01--, --02--, --03--,…, --09--, --10--, --11--, --12-- Month System.Int32

 

This table contains the partial list of XSD simple types. You can extend it by including other XSD types and, moreover, any types you derived from XSD simple types with XML schema derivation rules.

According the mapping above, for example, the <Points.Double …> point series XML element should contain points of xsd:double type for both x and y dimensions and these points will be loaded as points with System.Double x,y properties.

Point element itself is something like <Point x="2008-01-01" y="-20"/> with x and y required attributes.

Below is the excerpt from the example input XML data file:

<?xml version="1.0" encoding="utf-8"?>
<Items xmlns="urn:PointSeries-schema">
    <Points.Int.Double YName="y=x^2">
        <Point x="0" y="0"/>
        <Point x="1" y="0.01"/>
        …
    </Points.Int.Double>
    <Points.Date.Int YName="temperature" XName="Date">
        <Point x="2008-01-01" y="-20"/>
        <Point x="2008-02-01" y="-25"/>
        …
    </Points.Date.Int>
    <Points.Month.Double YName="2008 year month temperatures" XName="Month">
        <Point x="--01--" y="-20.8"/>
        <Point x="--02--" y="-25.2"/>
        …
    </Points.Month.Double>
    …
</Items>

Note that point series elements are decorated with optional YName and XName attributes intending to represent x and y dimensions labels.

Generic data point series data XML format is defined by the XML schema which excerpt follows:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" 
           xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <!-- Root element -->
  <xs:element name="Items" type="itemsType"/>
  
  <!-- Root element type -->
  <xs:complexType name="itemsType">
    <xs:choice maxOccurs="unbounded">
      <xs:element name="Points.Int" type="pointsIntIntType"/>
      <xs:element name="Points.Int.DateTime" type="pointsIntDttmType"/>
      …
      <xs:element name="Points.Double" type="pointsDblDblType"/>
      <xs:element name="Points.Double.Int" type="pointsDblIntType"/>
      …
    </xs:choice>
  </xs:complexType>
  
  <!-- Point Series Type attributes -->
  <xs:attributeGroup name="pointSetAttributes">
    <xs:attribute name="YName" type="xs:string" use="optional" />
    <xs:attribute name="XName" type="xs:string" use="optional" />
  </xs:attributeGroup>

  <!-- Point Series Types -->
  <xs:complexType name="pointsIntIntType">
      <xs:sequence>
        <xs:element minOccurs="1" maxOccurs="unbounded" name="Point">
          <xs:complexType>
            <xs:attribute name="x" type="xs:int" use="required" />
            <xs:attribute name="y" type="xs:int" use="required" />
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    <xs:attributeGroup ref="pointSetAttributes"/>
  </xs:complexType>
  <xs:complexType name="pointsIntDttmType">
      <xs:sequence>
        <xs:element minOccurs="1" maxOccurs="unbounded" name="Point">
          <xs:complexType>
            <xs:attribute name="x" type="xs:int" use="required" />
            <xs:attribute name="y" type="xs:dateTime" use="required" />
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    <xs:attributeGroup ref="pointSetAttributes"/>
  </xs:complexType>
  …  
  <xs:complexType name="pointsDblIntType">
      <xs:sequence>
        <xs:element minOccurs="1" maxOccurs="unbounded" name="Point">
          <xs:complexType>
            <xs:attribute name="x" type="xs:double" use="required" />
            <xs:attribute name="y" type="xs:int" use="required" />
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    <xs:attributeGroup ref="pointSetAttributes"/>
  </xs:complexType>
  <xs:complexType name="pointsDblDblType">
      <xs:sequence>
        <xs:element minOccurs="1" maxOccurs="unbounded" name="Point">
          <xs:complexType>
            <xs:attribute name="x" type="xs:double" use="required" />
            <xs:attribute name="y" type="xs:double" use="required" />
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    <xs:attributeGroup ref="pointSetAttributes"/>
  </xs:complexType>
  …
</xs:schema>

 

That schema defines <Items …> root element whose expected contents is defined by the XSD choice selector. You should modify the contents of the selector to just those point series element types your application expect.

In the code attached to the article the choice element contains all point series elements corresponding to the members of the Cartesian self-product of points base types from the Table 1.

The rest of the schema contains the long list of element type definitions. Each of these types defines the point series with specific x, y base types.

Now the data format is completely defined and we can proceed to the data reading.

Reading the Data

In the code attached to this post (see below) all data reading code is placed into Loader class library project producing XmlDataPointSeries.Loader assembly. Loader class contains the data reading/parsing code and two supplementary classes DataPoint and DataPointSeries provide the place to store the results.

That is the full Loader class code:

/// <summary>
/// Class to load DataPointSeries collection from a file.
/// </summary>
/// <remarks>
/// On error contains the string indicating what wrong is happen while loading.
/// </remarks>
public class Loader
{
  // XML Schema resource name.
  const string schemaFileName = "schema.xsd";
  // XML namespace must be used in XML data files.
  const string namespaceName = "urn:PointSeries-schema";

  /// <summary>
  /// Gets the error string.
  /// </summary>
  /// <remarks>
  /// List of error descritions happen on the input file loading or validation.
  /// </remarks>
  /// <value>The error string or null.</value>
  public string Errors { get; private set; }

  /// <summary>
  /// Loads DataPointSeries collection from the file specified.
  /// </summary>
  /// <param name="fileName">DataPointSeries collection XML file Name.</param>
  /// <returns>DataPointSeries collection on success; null on error.</returns>
  public DataPointSeries[] Load(string fileName)
  {
    Errors = null;
    XmlReader reader = null;
    try
    {
      // Get xml schema stream from the "schemaFileName" resource.
      Assembly assembly = Assembly.GetAssembly(this.GetType());
      ResourceManager rm = new ResourceManager(assembly.GetName().Name + ".g", assembly);
      using (XmlTextReader schemaReader = new XmlTextReader(rm.GetStream(schemaFileName)))
      {
        // Prepaire XmlReaderSettings for input file validation.
        XmlReaderSettings settings = new XmlReaderSettings();
        settings.ValidationType = ValidationType.Schema;
        settings.Schemas.Add(namespaceName, schemaReader);
        StringBuilder sbErrors = null;
        settings.ValidationEventHandler += (sender, e) =>
        {
          if (sbErrors == null)
            sbErrors = new StringBuilder();
          sbErrors.AppendFormat("Validation error: {1}{0}Line={2}, position={3}{0}"
            , System.Environment.NewLine, e.Exception.Message
            , e.Exception.LineNumber, e.Exception.LinePosition);
        };

        // Load and validate the file.
        reader = XmlReader.Create(fileName, settings);
        XDocument xml = XDocument.Load(reader);
        if (sbErrors != null)
        { // Validation error(s) occured.
          Errors = sbErrors.ToString();
          return null;
        }

        XNamespace xns = namespaceName;
        // Check the root element (i.e. Items in "urn:PointSeries-schema" xmlns).
        XElement rootElement = xml.Element(xns + "Items");
        if (rootElement == null)
        {
          Errors = string.Format("Root element {0} missed", xns + "Items");
          return null;
        }
        // Parse the Point.XXX elements.
        return rootElement.Elements().Select<XElement, DataPointSeries>(
          (item) => {
            // Parse item tag name for X/Y type strings.
            string xType, yType;
            getXYTypeStrings(item.Name.ToString(), out xType, out yType);
            // Optional attributes.
            var yName = item.Attribute("YName");
            var xName = item.Attribute("XName");
            DataPointSeries series = new DataPointSeries()
            {
              YName = yName == null ? "" : yName.Value,
              XName = xName == null ? "" : xName.Value,
              XTypeString = xType,
              YTypeString = yType
            };
            foreach (var pt in from pt in item.Elements(xns + "Point") select new { X = pt.Attribute("x").Value, Y = pt.Attribute("y").Value })
            {
              series.AddPoint(pt.X, pt.Y);
            }
            return series;
          }).ToArray();
      }
    }
    catch (Exception ex)
    {
      Errors = ex.Message;
    }
    finally
    {
      if (reader != null)
        reader.Close();
    }

    return null;
  }

  /// <summary>
  /// Parses the point series element tag and returns x,y type strings.
  /// </summary>
  /// <param name="tagName">Tag name.</param>
  /// <param name="xType">Output Type of the x-dimension.</param>
  /// <param name="yType">Output Type of the y-dimension.</param>
  static void getXYTypeStrings(string tagName, out string xType, out string yType)
  {
    int n = tagName.IndexOf('}');
    Debug.Assert(n > 0, "n > 0");
    const string pointsTagPrefix = "Points";
    int pointsTagPrefixLength = pointsTagPrefix.Length;
    Debug.Assert(tagName.Length > n + pointsTagPrefixLength + 1, "tagName.Length > n + pointsTagPrefixLength + 1");
    string xyTypes = tagName.Substring(n + pointsTagPrefixLength + 2);
    n = xyTypes.IndexOf('.');
    if (n < 0)
    {
      xType = xyTypes;
      yType = xyTypes;
    }
    else
    {
      xType = xyTypes.Substring(0, n);
      yType = xyTypes.Substring(n + 1);
    }
  }
}

 

This class isn’t static just because it stores the Errors string if any error happens during data reading. Load method does all interesting work. It’s based on the two pillars:

  1. Data is loaded and validated by XmlReader object with the help of the XML schema defined above.
  2. Loaded XML Data is parsed and stored into target location with the LINQ to XML.

This approach make the Loader class very generic and rather concise.

By design the Load method fails on any error occurred on file opening, reading, parsing and validating and stores the errors descriptions in the string returning by the Errors property. It composes all validation errors found by XmlReader into the Errors string; this give the user a chance to fix all errors at once.

The schema file is stored as the resource in the Loader assembly. Load method uses the ResourceManager class to get that resource stream and pass it to XmlReaderSettings class instance. This instance ValidationType property is set to ValidationType.Schema and ValidationEventHandler is attached to collect all validation error strings into StringBuilder instance.

XmlReader is created with reader = XmlReader.Create(fileName, settings) call and XML is loaded into memory with xml = XDocument.Load(reader). If no errors happen at this point the data is loaded and is valid against the schema.

Load method gets the root element with

XNamespace xns = namespaceName;
XElement rootElement = xml.Element(xns + "Items");

 

Note the XNamespace type variable: it assures that the Items element is defined in the right XML namespace. After that the Load method parses loaded XML and returns the result with

return rootElement.Elements().Select<XElement, DataPointSeries>(…)

 

statement. DataPointSeries instances are create by the lambda statement which

  1. Extracts the data series base types from the XElement tag name with getXYTypeStrings method.
  2. Gets optional attributes.
  3. Creates the instance of the DataPointSeries class.
  4. Fills that instance Points property with the call to DataPointSeries.AddPoint method.

AddPoint method is rather long. It takes string x, y values and converts them into .NET types basing on base type strings extracted from the XElement tag name. It consists of nested switch statements to process all assumed x, y types combinations. Certainly this makes the Loader class less generic but hides from the user some subtleties of converting XML schema types to .NET types (e.g. xsd:double type allows the NaN, INF and –INF values which can not be converted to the right System.Double values with Convert.ToDouble method or its counterparts). If you prefer you could resort to do not convert x, y values to specific types and store them as generic object type.

Using the Code

You can download this post source code here:

 

This code contains the Visual Studio 2008 SP1 solution targeted to .NET Framework 3.5 with three projects. Main part is the Loader class library project described above.

One other project is the simple Console Application which loads the XML data from the file pointed by the first command line argument and either reports errors or displays the results of XML data parsing.

Another project is more important. It’s the Unit Test project containing the tests for every data point series type supported by the Loader class. It provides you with examples of which data is currently supported by XML format in question and how they could look like.

Technorati Теги: ,,,

3月24日

OpenWPFChart library Beta 02 release uploaded

This release contains the following OpenWPFChart library project updates:
  1. Bezier spline control points calculation algorithm error fixed.
  2. Issues resulted in chart layout distortion on window resize in sample applications fixed.
  3. Descriptions of sample data file formats added.

Regards,
Oleg V. Polikarpotchkin

3月18日

OpenWPFChart library published

I’ve published OpenWPFChart library project at CodePlex.

The library defines the object model and the set of base components (parts) to assemble Charts in Windows Presentation Foundation. Parts set is extensible so the developer can add its own new components. Chart controls composed from the these parts could have different look and feel.

It also contains some Chart controls prebuilt from the parts.

Regards,

Oleg V. Polikarpotchkin

3月1日

Draw Closed Smooth Curve with Bezier Spline

December 17, 2008 I posted an article on how to draw the smooth curve through the set of 2D points with Bezier drawing primitives. Yesterday I got a question on how to draw a closed curve in the same manner. Indeed, there is a difference to open-ended curve.

Equations

Note: to make a sequence of individual Bezier curves to be a spline we should calculate Bezier control points so that the spline curve has two continuous derivatives at the knot points.

Bezier curve at the single interval is expressed as

B(t)=(1-t)3P0+3(1-t)2tP1+3(1-t)t2P2+t3P3

where t is in [0,1] and

1. P0 – first knot point

2. P1 – first control point (at first knot)

3. P2 – second control point (at second knot)

4. P3 – second knot point

First derivative is:

B’(t) = -3 (1 - t)2 P0 + 3 (3 t2 – 4t + 1) P1 + 3 (2t – 3t2) P2 + 3t2 P3

Second derivative is:

B’’(t) = 6 (1 - t) P0 + 3 (6t - 4) P1 + 3 (2 – 6t) P2+ 6 t P3

Let’s consider piece-wise Bezier curve on the interval with n (n > 2) points Pi (0,..,n-1) and n subintervals Si (0,..,n-1).

Si = (Pi, Pi+1) for (i=0,..,n-2)

Sn-1 = (Pn-1, P0) for (i=n-1)

Bezier curve at Si (i=0,..,n-2) will be 

Bi(t) = (1 - t)3 Pi + 3 (1-t)2 t P1i + 3 (1-t) t2 P2i+1 + t3 Pi+1; (i=0,..,n-2)

and the closing Bezier curve at Sn-1

Bi(t) = (1 - t)3 Pn-1 + 3 (1-t)2 t P1n-1 + 3 (1-t) t2 P20 + t3 P0

First derivative at Si (i=0,..,n-2) will be

B’i(t) = -3 (1-t)2 Pi + 3(3t2 - 4t + 1) P1i + 3(2t - 3t2) P2i+1+3t2 Pi+1

and at Sn-1

B’n-1(t) = -3 (1-t)2 Pn-1 + 3(3t2 - 4t + 1) P1n-1 + 3(2t - 3t2) P20+3t2 P0

First derivative continuity condition gives:

P1i + P2i = 2 Pi (i=0,..,n-1)                                                                                      (1)

Second derivative at Si (i=0,..,n-2) will be

B’’i (t) = 6 (1-t) Pi +6 (3t - 2) P1i + 6 (1 - 3t) P2i+1 + 6t Pi+1

and at Sn-1

B’’n-1 (t) = 6 (1-t) Pn-1 +6 (3t - 2) P1n-1 + 6 (1 - 3t) P20 + 6t P0

Second derivative continuity condition gives:

at P0

2 P10 + P1n-1 = 2 P20 + P21                                                                               (2.1)

at Pi (i=1,..,n-2)

2 P1i + P1i-1 = 2 P2i + P2i+1                                                                                (2.2)

and at Pn-1

2 P1n-1 + P1n-2 = 2 P2n-1 + P20                                                                          (2.3)

Excluding P2 form (2.1-3) with (1) we get the set of equations for P1

4 P10 + P11 + P1n-1 = 4 P0 + 2 P1 for P0

P1i-1 + 4 P1i + P1i+1 = 4 Pi + 2 Pi+1 for Pi (i=1,..,n-2)

P10 + P1n-2 + 4 P1n-1 = 4 Pn-1 + 2 P0 for Pn-1

We got the system with the matrix which looks like

4 1 0 0 ... 0 1

1 4 1 0 ... 0 0

0 1 4 1 ... 0 0

...............

1 0 0 0 ... 1 4

It's so-called "cyclic" system which can be solved as effectively as a tridiagonal system with the trick which you can find in the "Numerical Recipes", for example.

After P1's found it's easy to get P2's from (1).

Code and sample

You can download the code and sample application here:

It is Visual Studio 2008 solution targeted to .NET 3.5. It contains WPF Windows Application project designed to demonstrate sampled circle drawn with Bezier spline above.

DrawSampledCircle

Although I compiled this code in C# 3.0 I don’t see why it can’t be used without any modification in C# 2.0 and even in C# 1.0 if you remove keyword “static” from the class declarations.