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-VoigtT2BranchesPseudoVoigtPointsSet | yes | — | — | a single position | TDiffractionAngleAxis |
Asym. Pseudo-VoigtTAsymPseudoVoigtPointsSet | yes | — | — | a single position | TDiffractionAngleAxis |
Doniach-SunjicTDoniachSunjicPointsSet | yes | — | — | a single position | TDiffractionAngleAxis |
Exponentially Modified GaussianTEmgPointsSet | yes | — | — | a single position | TDiffractionAngleAxis |
GaussianTGaussPointsSet | yes | — | — | a single position | TDiffractionAngleAxis |
LorentzianTLorentzPointsSet | yes | — | — | a single position | TDiffractionAngleAxis |
MoffatTMoffatPointsSet | yes | — | — | a single position | TDiffractionAngleAxis |
Pearson VIITPearson7PointsSet | yes | — | — | a single position | TDiffractionAngleAxis |
Pseudo-VoigtTPseudoVoigtPointsSet | yes | — | — | a single position | TDiffractionAngleAxis |
Skewed GaussianTSkewedGaussianPointsSet | yes | — | — | a single position | TDiffractionAngleAxis |
Step (erf)TStepPointsSet | yes | — | — | a single position | TDiffractionAngleAxis |
User DefinedTUserPointsSet | yes | — | — | a single position | TIdentityAxis |
VoigtTVoigtPointsSet | yes | — | — | a single position | TDiffractionAngleAxis |
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 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 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
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 ICreateUserPointsSetDlg["ICreateUserPointsSetDlg"]
<<interface>> ICreateUserPointsSetDlg
class ICurveTypeParametersFactory["ICurveTypeParametersFactory"]
<<interface>> ICurveTypeParametersFactory
class ICurveTypeStorage["ICurveTypeStorage"]
<<interface>> ICurveTypeStorage
class IExpressionParser["IExpressionParser"]
<<interface>> IExpressionParser
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 TUserPointsSet["TUserPointsSet"]
class TUserPointsSetPropDlg["TUserPointsSetPropDlg"]
TConfigurablePointsSet <|-- TConfigurableUserPointsSet
TForm <|-- TCreateUserPointsSetDlg
TInterfacedObject <|-- TCreateUserPointsSetDlgAdapter
ICreateUserPointsSetDlg <|.. TCreateUserPointsSetDlgAdapter
TInterfacedObject <|-- TCurveTypeParametersFactory
ICurveTypeParametersFactory <|.. TCurveTypeParametersFactory
TInterfacedObject <|-- TCurveTypeStorageAdapter
ICurveTypeStorage <|.. TCurveTypeStorageAdapter
TInterfacedObject <|-- TExpressionParserAdapter
IExpressionParser <|.. TExpressionParserAdapter
TConfigurablePointsSet <|-- TNonConfigurablePointsSet
TSpecialCurveParameter <|-- TUserCurveParameter
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 dlg as ICreateUserPointsSetDlg
participant parser as IExpressionParser
participant factory as ICurveTypeParametersFactory
participant store as ICurveTypeStorage
participant props as TUserPointsSetPropDlg
ui->>iter: which registered type was clicked
iter-->>ui: GetCurrentCurveClass
ui->>conf: GetConfigurablePointsSet, HasConfigurableParameters
alt the type configures itself
ui->>conf: ShowConfigurationDialog
conf->>dlg: ShowModal - a name and a formula in x
conf->>parser: ParseExpression
alt the formula does not parse
parser-->>conf: nil, the message already shown
conf->>dlg: re-open, never crash
else it parses
conf->>factory: CreateUserCurveType
conf->>store: AddCurveType
conf->>props: ShowModal - roles and initial values
alt mrOk
conf->>store: UpdateCurveType
else mrRetry
conf->>store: DeleteCurveType, back to the first dialog
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.