Apophysis-AV/System/MathExpressions.pas

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.