Fit

Interactive curve fitting for Linux, Windows and macOS

How to add a curve type

A curve type is one unit. It subclasses TNamedPointsSet, answers a few questions about itself, and registers itself from its own initialization section. Nothing else is edited — the menu, the factory and the compatibility rules are all derived from what registered.

flowchart TB
    unit["`**Your unit**
class(TNamedPointsSet)`"]
    init["`**initialization**
RegisterCurveType(TYours)`"]
    reg["`**TCurveTypesSingleton**
the curve factory`"]
    menu["`**The Curve Type menu**
grouped by GetCurveTypeGroup`"]
    engine["`**The engine**
instantiates by type id`"]
    loss["`**Objective compatibility**
derived from AmplitudeIsUnbounded`"]
    axis["`**Displayed axis**
from CreatePreferredAxis`"]
    unit --> init --> reg
    reg --> menu
    reg --> engine
    reg --> loss
    reg --> axis

The questions a curve type answers

Each has a sensible default, so a type only overrides what differs. These are capabilities, not a list of type names: adding a type needs no edit to any compatibility table.

Type Analytic Group Unbounded amplitude Placed by Preferred axis
2 br. Pseudo-Voigt
T2BranchesPseudoVoigtPointsSet
yesa single positionTDiffractionAngleAxis
Asym. Pseudo-Voigt
TAsymPseudoVoigtPointsSet
yesa single positionTDiffractionAngleAxis
Doniach-Sunjic
TDoniachSunjicPointsSet
yesa single positionTDiffractionAngleAxis
Exponentially Modified Gaussian
TEmgPointsSet
yesa single positionTDiffractionAngleAxis
Gaussian
TGaussPointsSet
yesa single positionTDiffractionAngleAxis
Lorentzian
TLorentzPointsSet
yesa single positionTDiffractionAngleAxis
Moffat
TMoffatPointsSet
yesa single positionTDiffractionAngleAxis
Pearson VII
TPearson7PointsSet
yesa single positionTDiffractionAngleAxis
Pseudo-Voigt
TPseudoVoigtPointsSet
yesa single positionTDiffractionAngleAxis
Skewed Gaussian
TSkewedGaussianPointsSet
yesa single positionTDiffractionAngleAxis
Step (erf)
TStepPointsSet
yesa single positionTDiffractionAngleAxis
User Defined
TUserPointsSet
yesa single positionTIdentityAxis
Voigt
TVoigtPointsSet
yesa single positionTDiffractionAngleAxis

13 curve types are registered in this build.

No registered type asks for a menu group, so the Group column is empty throughout. A type that returns a name from GetCurveTypeGroup gets a submenu of that name — which is what a pack contributing several related types wants, rather than crowding the flat list.

The curve classes

Read from the units themselves. A curve type is a point set first: TPointsSet holds the points, TCurvePointsSet adds the parameters a minimizer varies, and TNamedPointsSet adds the questions above. TCurveTypesSingleton is the registry every type registers into, and the three interfaces it implements are how the menu, the factory and the engine reach it — none of them names a curve class.

classDiagram
    direction LR
    class TInterfacedObject["TInterfacedObject"]
    <<external>> TInterfacedObject
    class TObject["TObject"]
    <<external>> TObject
    class TSelfCopiedComponent["TSelfCopiedComponent"]
    <<external>> TSelfCopiedComponent
    class ICurveFactory["ICurveFactory"]
    <<interface>> ICurveFactory
    class ICurveTypeIterator["ICurveTypeIterator"]
    <<interface>> ICurveTypeIterator
    class ICurveTypeSelector["ICurveTypeSelector"]
    <<interface>> ICurveTypeSelector
    class T2BranchesPseudoVoigtPointsSet["T2BranchesPseudoVoigtPointsSet"]
    class TAsymPseudoVoigtPointsSet["TAsymPseudoVoigtPointsSet"]
    class TCurvePointsSet["TCurvePointsSet"]
    class TCurveTypeExplanationProvider["TCurveTypeExplanationProvider"]
    class TCurveTypesSingleton["TCurveTypesSingleton"]
    class TDoniachSunjicPointsSet["TDoniachSunjicPointsSet"]
    class TEmgPointsSet["TEmgPointsSet"]
    class TFormulaPointsSet["TFormulaPointsSet"]
    class TGaussPointsSet["TGaussPointsSet"]
    class TLorentzPointsSet["TLorentzPointsSet"]
    class TMoffatPointsSet["TMoffatPointsSet"]
    class TNamedPointsSet["TNamedPointsSet"]
    class TNeutronPointsSet["TNeutronPointsSet"]
    class TPearson7PointsSet["TPearson7PointsSet"]
    class TPointsSet["TPointsSet"]
    class TPseudoVoigtPointsSet["TPseudoVoigtPointsSet"]
    class TSkewedGaussianPointsSet["TSkewedGaussianPointsSet"]
    class TStepPointsSet["TStepPointsSet"]
    class TTitlePointsSet["TTitlePointsSet"]
    class TVoigtPointsSet["TVoigtPointsSet"]
    TNamedPointsSet <|-- T2BranchesPseudoVoigtPointsSet
    TPseudoVoigtPointsSet <|-- TAsymPseudoVoigtPointsSet
    TTitlePointsSet <|-- TCurvePointsSet
    TObject <|-- TCurveTypeExplanationProvider
    TInterfacedObject <|-- TCurveTypesSingleton
    ICurveFactory <|.. TCurveTypesSingleton
    ICurveTypeIterator <|.. TCurveTypesSingleton
    ICurveTypeSelector <|.. TCurveTypesSingleton
    TFormulaPointsSet <|-- TDoniachSunjicPointsSet
    TFormulaPointsSet <|-- TEmgPointsSet
    TNamedPointsSet <|-- TFormulaPointsSet
    TNamedPointsSet <|-- TGaussPointsSet
    TGaussPointsSet <|-- TLorentzPointsSet
    TFormulaPointsSet <|-- TMoffatPointsSet
    TCurvePointsSet <|-- TNamedPointsSet
    TPointsSet <|-- TNeutronPointsSet
    TFormulaPointsSet <|-- TPearson7PointsSet
    TSelfCopiedComponent <|-- TPointsSet
    TNamedPointsSet <|-- TPseudoVoigtPointsSet
    TFormulaPointsSet <|-- TSkewedGaussianPointsSet
    TFormulaPointsSet <|-- TStepPointsSet
    TNeutronPointsSet <|-- TTitlePointsSet
    TFormulaPointsSet <|-- TVoigtPointsSet

Curve types the user defines

A type whose formula is typed at run time still arrives through the same registry. The configuration lives in a separate class (GetConfigurablePointsSet) rather than on the curve itself: these are class-level questions asked before any instance exists, and Free Pascal has neither multiple inheritance nor class methods in interfaces.

classDiagram
    direction LR
    class TForm["TForm"]
    <<external>> TForm
    class TInterfacedObject["TInterfacedObject"]
    <<external>> TInterfacedObject
    class TNamedPointsSet["TNamedPointsSet"]
    <<external>> TNamedPointsSet
    class TSpecialCurveParameter["TSpecialCurveParameter"]
    <<external>> TSpecialCurveParameter
    class ICurveTypeParametersFactory["ICurveTypeParametersFactory"]
    <<interface>> ICurveTypeParametersFactory
    class ICurveTypeStorage["ICurveTypeStorage"]
    <<interface>> ICurveTypeStorage
    class IExpressionParser["IExpressionParser"]
    <<interface>> IExpressionParser
    class IUserCurveFormulaDlg["IUserCurveFormulaDlg"]
    <<interface>> IUserCurveFormulaDlg
    class IUserCurveRolesDlg["IUserCurveRolesDlg"]
    <<interface>> IUserCurveRolesDlg
    class TConfigurablePointsSet["TConfigurablePointsSet"]
    class TConfigurableUserPointsSet["TConfigurableUserPointsSet"]
    class TCreateUserPointsSetDlg["TCreateUserPointsSetDlg"]
    class TCreateUserPointsSetDlgAdapter["TCreateUserPointsSetDlgAdapter"]
    class TCurveTypeParametersFactory["TCurveTypeParametersFactory"]
    class TCurveTypeStorageAdapter["TCurveTypeStorageAdapter"]
    class TExpressionParserAdapter["TExpressionParserAdapter"]
    class TNonConfigurablePointsSet["TNonConfigurablePointsSet"]
    class TUserCurveParameter["TUserCurveParameter"]
    class TUserCurveRolesDlgAdapter["TUserCurveRolesDlgAdapter"]
    class TUserPointsSet["TUserPointsSet"]
    class TUserPointsSetPropDlg["TUserPointsSetPropDlg"]
    TConfigurablePointsSet <|-- TConfigurableUserPointsSet
    TForm <|-- TCreateUserPointsSetDlg
    TInterfacedObject <|-- TCreateUserPointsSetDlgAdapter
    IUserCurveFormulaDlg <|.. TCreateUserPointsSetDlgAdapter
    TInterfacedObject <|-- TCurveTypeParametersFactory
    ICurveTypeParametersFactory <|.. TCurveTypeParametersFactory
    TInterfacedObject <|-- TCurveTypeStorageAdapter
    ICurveTypeStorage <|.. TCurveTypeStorageAdapter
    TInterfacedObject <|-- TExpressionParserAdapter
    IExpressionParser <|.. TExpressionParserAdapter
    TConfigurablePointsSet <|-- TNonConfigurablePointsSet
    TSpecialCurveParameter <|-- TUserCurveParameter
    TInterfacedObject <|-- TUserCurveRolesDlgAdapter
    IUserCurveRolesDlg <|.. TUserCurveRolesDlgAdapter
    TNamedPointsSet <|-- TUserPointsSet
    TForm <|-- TUserPointsSetPropDlg

Creating one is two dialogs, and every failure has a defined landing place — an unparseable formula reopens the first dialog rather than raising, and a cancelled setup leaves the type unselected instead of half-registered.

sequenceDiagram
    autonumber
    participant ui as TFormMain
    participant iter as ICurveTypeIterator
    participant conf as TConfigurableUserPointsSet
    participant flow as RunUserCurveFlow
    participant dlg as IUserCurveFormulaDlg
    participant parser as IExpressionParser
    participant factory as ICurveTypeParametersFactory
    participant store as ICurveTypeStorage
    participant roles as IUserCurveRolesDlg
    ui->>iter: which registered type was clicked
    iter-->>ui: GetCurrentCurveClass
    ui->>conf: GetConfigurablePointsSet, HasConfigurableParameters
    alt the type configures itself
        ui->>conf: ShowConfigurationDialog
        conf->>flow: the five collaborators, and nothing else
        flow->>dlg: Ask - a name and a formula in x
        alt cancelled
            dlg-->>flow: daCancelled, and the definition is abandoned
        else confirmed
            flow->>parser: ParseExpression
            alt the formula does not parse
                parser-->>flow: nil, the message already shown
                flow->>dlg: Ask again - nothing has been stored yet
            else it parses
                flow->>factory: CreateUserCurveType
                flow->>store: AddCurveType
                flow->>roles: Ask - which parameter plays which role
                alt daAccepted
                    flow->>store: UpdateCurveType
                else daStartAgain
                    flow->>store: DeleteCurveType, then Ask the formula again
                else daCancelled
                    flow->>flow: the stored type is left as it is
                end
            end
        end
    end
    alt configuration was cancelled and HasDefaults
        ui->>conf: SetDefaults
    else cancelled with no defaults
        ui->>ui: say so, and leave the type unselected
    end

Full instructions, including the traps: adding a curve model · writing a module · a complete working example.