338 lines
9.8 KiB
ObjectPascal
338 lines
9.8 KiB
ObjectPascal
{ Apophysis AV "Phoenix Edition" Copyright (C) 2021-2022 Alice V. Koryagina }
|
|
|
|
unit MathExpressions;
|
|
|
|
interface
|
|
|
|
uses
|
|
System.Rtti, System.SysUtils, System.Bindings.EvalProtocol,
|
|
System.Bindings.Evaluator, System.Bindings.EvalSys, System.Bindings.Methods;
|
|
|
|
function CalculateExpression(const Expr: string): string;
|
|
|
|
var
|
|
InDegrees: boolean;
|
|
|
|
implementation
|
|
|
|
uses
|
|
System.Math, Windows, Translation;
|
|
|
|
var
|
|
LScope: IScope;
|
|
|
|
function Sine: IInvokable;
|
|
begin
|
|
Result := MakeInvokable(
|
|
function(Args: TArray<IValue>): IValue
|
|
var
|
|
IAValue: IValue;
|
|
ANum: Double;
|
|
begin
|
|
// AV: check the number of passed parameters
|
|
if Length(Args) <> 1 then begin
|
|
MessageBox(0, PChar(Format(TextByKey('formula-wrongargscount'),
|
|
['Sin()', 1, Length(Args)])), 'Apophysis AV', 48);
|
|
exit;
|
|
end;
|
|
|
|
IAValue := Args[0];
|
|
|
|
try
|
|
ANum := IAValue.GetValue.AsExtended; // AV: check the parameter type
|
|
if InDegrees then // AV: translate the parameter into radians
|
|
ANum := DegToRad(ANum);
|
|
Exit(TValueWrapper.Create(RoundTo(sin(ANum), -6)));
|
|
except
|
|
MessageBox(0, PChar('Sin(): ' + TextByKey('formula-wrongdatatype')),
|
|
'Apophysis AV', 16);
|
|
end;
|
|
end
|
|
);
|
|
end;
|
|
|
|
function CoSine: IInvokable;
|
|
begin
|
|
Result := MakeInvokable(
|
|
function(Args: TArray<IValue>): IValue
|
|
var
|
|
IAValue: IValue;
|
|
ANum: Double;
|
|
begin
|
|
// AV: check the number of passed parameters
|
|
if Length(Args) <> 1 then begin
|
|
MessageBox(0, PChar(Format(TextByKey('formula-wrongargscount'),
|
|
['Cos()', 1, Length(Args)])), 'Apophysis AV', 48);
|
|
exit;
|
|
end;
|
|
|
|
IAValue := Args[0];
|
|
|
|
try
|
|
ANum := IAValue.GetValue.AsExtended;
|
|
if InDegrees then // AV: translate the parameter into radians
|
|
ANum := DegToRad(ANum);
|
|
Exit(TValueWrapper.Create(RoundTo(cos(ANum), -6)));
|
|
except
|
|
MessageBox(0, PChar('Cos(): ' + TextByKey('formula-wrongdatatype')),
|
|
'Apophysis AV', 16);
|
|
end;
|
|
end
|
|
);
|
|
end;
|
|
|
|
function ArcSine: IInvokable;
|
|
begin
|
|
Result := MakeInvokable(
|
|
function(Args: TArray<IValue>): IValue
|
|
var
|
|
IAValue: IValue;
|
|
AValue: Double;
|
|
begin
|
|
// AV: check the number of passed parameters
|
|
if Length(Args) <> 1 then begin
|
|
MessageBox(0, PChar(Format(TextByKey('formula-wrongargscount'),
|
|
['ArcSin()', 1, Length(Args)])), 'Apophysis AV', 48);
|
|
exit;
|
|
end;
|
|
|
|
IAValue := Args[0];
|
|
|
|
try
|
|
AValue := IAValue.GetValue.AsExtended;
|
|
if InRange(AValue, -1, 1) then
|
|
begin
|
|
AValue := arcsin(AValue);
|
|
if InDegrees then
|
|
AValue := RadToDeg(AValue);
|
|
Exit(TValueWrapper.Create(RoundTo(AValue, -6)));
|
|
end
|
|
else
|
|
MessageBox(0, PChar('ArcSin(): ' + TextByKey('formula-outofrange')),
|
|
'Apophysis AV', 48);
|
|
except
|
|
MessageBox(0, PChar('ArcSin(): ' + TextByKey('formula-wrongdatatype')),
|
|
'Apophysis AV', 16);
|
|
end;
|
|
end
|
|
);
|
|
end;
|
|
|
|
function ArcCoSine: IInvokable;
|
|
begin
|
|
Result := MakeInvokable(
|
|
function(Args: TArray<IValue>): IValue
|
|
var
|
|
IAValue: IValue;
|
|
AValue: Double;
|
|
begin
|
|
//AV: check the number of passed parameters
|
|
if Length(Args) <> 1 then begin
|
|
MessageBox(0, PChar(Format(TextByKey('formula-wrongargscount'),
|
|
['ArcCos()', 1, Length(Args)])), 'Apophysis AV', 48);
|
|
exit;
|
|
end;
|
|
|
|
IAValue := Args[0];
|
|
|
|
try
|
|
AValue := IAValue.GetValue.AsExtended;
|
|
if InRange(AValue, -1, 1) then
|
|
begin
|
|
AValue := arccos(AValue);
|
|
if InDegrees then
|
|
AValue := RadToDeg(AValue);
|
|
Exit(TValueWrapper.Create(RoundTo(AValue, -6)));
|
|
end
|
|
else
|
|
MessageBox(0, PChar('ArcCos(): ' + TextByKey('formula-outofrange')),
|
|
'Apophysis AV', 48);
|
|
except
|
|
MessageBox(0, PChar('ArcCos(): ' + TextByKey('formula-wrongdatatype')),
|
|
'Apophysis AV', 16);
|
|
end;
|
|
end
|
|
);
|
|
end;
|
|
|
|
function ArcTangentYX: IInvokable;
|
|
begin
|
|
Result := MakeInvokable(
|
|
function(Args: TArray<IValue>): IValue
|
|
var
|
|
IYValue, IXValue: IValue;
|
|
AValue: Double;
|
|
begin
|
|
// AV: check the number of passed parameters
|
|
if Length(Args) <> 2 then begin
|
|
MessageBox(0, PChar(Format(TextByKey('formula-wrongargscount'),
|
|
['ArcTan2()', 2, Length(Args)])), 'Apophysis AV', 48);
|
|
exit;
|
|
end;
|
|
|
|
IYValue := Args[0];
|
|
IXValue := Args[1];
|
|
|
|
try
|
|
AValue := arctan2(IYValue.GetValue.AsExtended,
|
|
IXValue.GetValue.AsExtended);
|
|
if InDegrees then
|
|
AValue := RadToDeg(AValue);
|
|
Exit(TValueWrapper.Create(RoundTo(AValue, -6)));
|
|
except
|
|
MessageBox(0, PChar('ArcTan2(): ' + TextByKey('common-invalidformat')),
|
|
'Apophysis AV', 16);
|
|
end;
|
|
end
|
|
);
|
|
end;
|
|
|
|
function SquareRoot: IInvokable;
|
|
begin
|
|
Result := MakeInvokable(
|
|
function(Args: TArray<IValue>): IValue
|
|
var
|
|
IAValue: IValue;
|
|
ANum: Double;
|
|
begin
|
|
// AV: check the number of passed parameters
|
|
if Length(Args) <> 1 then begin
|
|
MessageBox(0, PChar(Format(TextByKey('formula-wrongargscount'),
|
|
['Sqrt()', 1, Length(Args)])), 'Apophysis AV', 48);
|
|
exit;
|
|
end;
|
|
|
|
IAValue := Args[0];
|
|
|
|
try
|
|
ANum := IAValue.GetValue.AsExtended;
|
|
if ANum >= 0 then
|
|
Exit(TValueWrapper.Create(RoundTo(sqrt(ANum), -6)))
|
|
else
|
|
MessageBox(0, PChar('Sqrt(): ' + TextByKey('formula-unsigned')),
|
|
'Apophysis AV', 48);
|
|
except
|
|
MessageBox(0, PChar('Sqrt(): ' + TextByKey('formula-wrongdatatype')),
|
|
'Apophysis AV', 16);
|
|
end;
|
|
end
|
|
);
|
|
end;
|
|
|
|
function NatLog: IInvokable;
|
|
begin
|
|
Result := MakeInvokable(
|
|
function(Args: TArray<IValue>): IValue
|
|
var
|
|
IAValue: IValue;
|
|
ANum: Double;
|
|
begin
|
|
// AV: check the number of passed parameters
|
|
if Length(Args) <> 1 then begin
|
|
MessageBox(0, PChar(Format(TextByKey('formula-wrongargscount'),
|
|
['Ln()', 1, Length(Args)])), 'Apophysis AV', 48);
|
|
exit;
|
|
end;
|
|
|
|
IAValue := Args[0];
|
|
|
|
try
|
|
ANum := IAValue.GetValue.AsExtended;
|
|
if ANum > 0 then
|
|
Exit(TValueWrapper.Create(RoundTo(ln(ANum), -6)))
|
|
else
|
|
MessageBox(0, PChar('Ln(): ' + TextByKey('formula-unsigned')),
|
|
'Apophysis AV', 48);
|
|
except
|
|
MessageBox(0, PChar('Ln(): ' + TextByKey('formula-wrongdatatype')),
|
|
'Apophysis AV', 16);
|
|
end;
|
|
end
|
|
);
|
|
end;
|
|
|
|
function PowerXY: IInvokable;
|
|
begin
|
|
Result := MakeInvokable(
|
|
function(Args: TArray<IValue>): IValue
|
|
var
|
|
IYValue, IXValue: IValue;
|
|
ANum: Double;
|
|
begin
|
|
// AV: check the number of passed parameters
|
|
if Length(Args) <> 2 then begin
|
|
MessageBox(0, PChar(Format(TextByKey('formula-wrongargscount'),
|
|
['Power()', 2, Length(Args)])), 'Apophysis AV', 48);
|
|
exit;
|
|
end;
|
|
|
|
IXValue := Args[0];
|
|
IYValue := Args[1];
|
|
|
|
try
|
|
ANum := IXValue.GetValue.AsExtended;
|
|
if ANum >= 0 then
|
|
Result := TValueWrapper.Create(RoundTo(power(ANum,
|
|
IYValue.GetValue.AsExtended), -6))
|
|
else
|
|
MessageBox(0, PChar('Power(): ' + TextByKey('formula-unsigned')),
|
|
'Apophysis AV', 48);
|
|
except
|
|
MessageBox(0, PChar('Power(): ' + TextByKey('common-invalidformat')),
|
|
'Apophysis AV', 16);
|
|
end;
|
|
end
|
|
);
|
|
end;
|
|
|
|
procedure RegisterMathFunctions;
|
|
begin
|
|
TBindingMethodsFactory.RegisterMethod(TMethodDescription.Create(
|
|
Sine, 'sin', 'sin', '', True, '', nil));
|
|
TBindingMethodsFactory.RegisterMethod(TMethodDescription.Create(
|
|
CoSine, 'cos', 'cos', '', True, '', nil));
|
|
TBindingMethodsFactory.RegisterMethod(TMethodDescription.Create(
|
|
ArcSine, 'arcsin', 'arcsin', '', True, '', nil));
|
|
TBindingMethodsFactory.RegisterMethod(TMethodDescription.Create(
|
|
ArcCoSine, 'arccos', 'arccos', '', True, '', nil));
|
|
TBindingMethodsFactory.RegisterMethod(TMethodDescription.Create(
|
|
ArcTangentYX, 'arctan2', 'arctan2', '', True, '', nil));
|
|
TBindingMethodsFactory.RegisterMethod(TMethodDescription.Create(
|
|
SquareRoot, 'sqrt', 'sqrt', '', True, '', nil));
|
|
TBindingMethodsFactory.RegisterMethod(TMethodDescription.Create(
|
|
PowerXY, 'power', 'power', '', True, '', nil));
|
|
TBindingMethodsFactory.RegisterMethod(TMethodDescription.Create(
|
|
NatLog, 'ln', 'ln', '', True, '', nil));
|
|
|
|
LScope := BasicOperators;
|
|
TDictionaryScope(LScope).Map.Add('pi', TValueWrapper.Create(pi));
|
|
TDictionaryScope(LScope).Map.Add('exp', TValueWrapper.Create(exp(1)));
|
|
// AV: add the registered methods
|
|
LScope := TNestedScope.Create(LScope, TBindingMethodsFactory.GetMethodScope);
|
|
end;
|
|
|
|
function CalculateExpression(const Expr: string): string;
|
|
var
|
|
LCompiledExpr : ICompiledBinding;
|
|
LResult : TValue;
|
|
begin
|
|
Result := '';
|
|
|
|
try
|
|
LCompiledExpr := Compile(Expr, LScope);
|
|
LResult := LCompiledExpr.Evaluate(LScope, nil, nil).GetValue;
|
|
if not LResult.IsEmpty then
|
|
Result := LResult.ToString;
|
|
except
|
|
Result := '';
|
|
MessageBox(0, PChar(TextByKey('formula-cannotevaluate')),
|
|
'Apophysis AV', 16);
|
|
end;
|
|
end;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
initialization
|
|
RegisterMathFunctions;
|
|
|
|
end.
|