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 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.