home *** CD-ROM | disk | FTP | other *** search
- unit mathcomp;
-
- interface
-
- uses
- Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
-
- type
- TVector2D = record
- x,y: double;
- end;
-
- TVector3D = record
- X,Y,Z: Double;
- end;
-
- TTokenType = (ttUnknown,ttOperation,ttVariable,ttConstant);
-
- //TVariableList
- PVariableRecord = ^TVariableRecord;
- TVariableRecord = record
- VarData: Double;
- VarName: String;
- end;
-
- //Forward declarations
- TExpressionTree = class;
-
- TVarList = class(TList)
- private
- function GetVars(Index: String): PVariableRecord;
- public
- property Vars[Index: String]: PVariableRecord read GetVars;
- //Methods
- destructor Destroy; override;
- function GetIndex(AValue: String): Integer;
- function NewVar(AName: String; AData: Double): PVariableRecord;
- end;
-
- TExpressionNode = class(TObject)
- private
- FValue: String;
- FParent: TExpressionNode;
- FTokenType: TTokenType;
- FChildren: TList;
- FValidValue: Boolean;
- FNumValue: Double;
- FVarPointer: PVariableRecord;
- FTree: TExpressionTree;
- public
- property Value: String read FValue write FValue;
- property Parent: TExpressionNode read FParent write FParent;
- property TokenType: TTokenType read FTokenType write FTokenType;
- property Children: TList read FChildren write FChildren;
- property ValidValue: Boolean read FValidValue write FValidValue;
- property NumValue: Double read FNumValue write FNumValue;
- property VarPointer: PVariableRecord read FVarPointer write FVarPointer;
- property Tree: TExpressionTree read FTree write FTree;
- //Methods
- function CreateChild: TExpressionNode;
- function Evaluate(SuppressErrors,UseVars: Boolean): Double;
- constructor Create(AOwner: TExpressionNode; ATree: TExpressionTree);
- destructor Destroy; override;
- end;
-
- TExpressionTree = class(TPersistent)
- private
- FTopNode: TExpressionNode;
- FExpression: String;
- FCheckSyntax: Boolean;
- FTokens: TStringList;
- FVarList: TVarList;
- procedure SetExpression(AValue: String);
- public
- property TopNode: TExpressionNode read FTopNode write FTopNode;
- property Expression: String read FExpression write SetExpression;
- property CheckSyntax: Boolean read FCheckSyntax write FCheckSyntax;
- property Tokens: TStringList read FTokens write FTokens;
- property VarList: TVarList read FVarList write FVarList;
- //Methods
- constructor Create;
- destructor Destroy; override;
- procedure MakeTokens;
- procedure RemoveBadTokens;
- end;
-
- TDataSet2D = class(TObject)
- private
- FData: pointer;
- FCount: Longint;
- function GetData(Index: Longint): TVector2D;
- procedure SetData(Index: Longint; AValue: TVector2D);
- procedure SetCount(AValue: longint);
- public
- property Data[Index: longint]: TVector2D read GetData write SetData;
- property Count: longint read FCount write SetCount;
- constructor Create(ACount: Longint);
- destructor Destroy; override;
- end;
-
- TDataSet3D = class(TObject)
- private
- FData: pointer;
- FCount: Longint;
- function GetData(Index: Longint): TVector3D;
- procedure SetData(Index: Longint; AValue: TVector3D);
- procedure SetCount(AValue: longint);
- public
- property Data[Index: longint]: TVector3D read GetData write SetData;
- property Count: longint read FCount write SetCount;
- constructor Create(ACount: Longint);
- destructor Destroy; override;
- end;
-
- TDataLabel = class(TObject)
- private
- FData: String;
- Fx,Fy: integer;
- public
- property Data: string read FData write FData;
- property x: integer read Fx write Fx;
- property y: integer read Fy write Fy;
- end;
-
- {TAxesView}
-
- TAxesAlign = (aaPositive,aaNegative);
- TCoord2DFunc = function (InV: TVector2D): TVector2D of Object;
- TCoord3DFunc = function (InV: TVector3D): TVector3D of Object;
-
- TAxesView = class(TCustomControl)
- private
- { Private declarations }
- FXMin,FXMax,FXScale,FYMin,FYMax,FYScale: double;
- FShowGrid,FShowAxes,FShowLabels,FAutoUpdate: Boolean;
- FXAxisColor,FYAxisColor,FGridColor: TColor;
- FOnGetMathCoord,FOnGetRealCoord: TCoord2DFunc;
- FRXScale,FRYScale: Double;
- FOrigin: TVector2D;
- FGrid: TDataSet2D;
- FLabels: TList;
- FDecimals: Integer;
- FAlignLabelX,FAlignLabelY: TAxesAlign;
- FOnPaint: TNotifyEvent;
- procedure SetXMin(AValue: Double);
- procedure SetXMax(AValue: Double);
- procedure SetXScale(AValue: Double);
- procedure SetYMin(AValue: Double);
- procedure SetYMax(AValue: Double);
- procedure SetYScale(AValue: Double);
- procedure SetShowGrid(AValue: Boolean);
- procedure SetShowAxes(AValue: Boolean);
- procedure SetShowLabels(AValue: Boolean);
- procedure SetXAxisColor(AValue: TColor);
- procedure SetYAxisColor(AValue: TColor);
- procedure SetGridColor(AValue: TColor);
- procedure SetAlignLabelX(AValue: TAxesAlign);
- procedure SetAlignLabelY(AValue: TAxesAlign);
- procedure SetDecimals(AValue: Integer);
- procedure FontChange(Sender: TObject); virtual;
- //inherited methods
- protected
- { Protected declarations }
- procedure DrawAxes; virtual;
- procedure DrawGrid; virtual;
- procedure DrawLabels; virtual;
- public
- { Public declarations }
- property Labels: TList read FLabels write FLabels;
- property RXScale: double read FRXScale;
- property RYScale: double read FRYScale;
- property Origin: TVector2D read FOrigin;
- property Grid: TDataSet2D read FGrid;
- //Methods
- procedure DoAutoPan(X,Y: Integer); virtual;
- procedure Zoom(Percent: Integer);
- procedure SetScale(AXMin,AXMax,AXScale,AYMin,AYMax,AYScale: Double);
- function GetMathCoord(InV: TVector2D): TVector2D; virtual;
- function GetRealCoord(InV: TVector2D): TVector2D; virtual;
- procedure RecalcScale; virtual;
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure Paint; override;
- procedure WMSize(var Message: TWMSize); message WM_SIZE;
- published
- { Published declarations }
- property AutoUpdate: Boolean read FAutoUpdate write FAutoUpdate;
- property AlignLabelX: TAxesAlign read FAlignLabelX write SetAlignLabelX;
- property AlignLabelY: TAxesAlign read FAlignLabelY write SetAlignLabelY;
- property XMin: double read FXMin write SetXMin;
- property XMax: double read FXMax write SetXMax;
- property XScale: double read FXScale write SetXScale;
- property YMin: double read FYMin write SetYMin;
- property YMax: double read FYMax write SetYMax;
- property YScale: double read FYScale write SetYScale;
- property ShowGrid: Boolean read FShowGrid write SetShowGrid;
- property ShowAxes: Boolean read FShowAxes write SetShowAxes;
- property ShowLabels: Boolean read FShowLabels write SetShowLabels;
- property XAxisColor: TColor read FXAxisColor write SetXAxisColor;
- property YAxisColor: TColor read FYAxisColor write SetYAxisColor;
- property GridColor: TColor read FGridColor write SetGridColor;
- property Decimals: Integer read FDecimals write SetDecimals;
- //Inherited properties to be published
- property Color;
- property Font;
- property Align;
- //Events
- property OnGetMathCoord: TCoord2DFunc read FOnGetMathCoord write FOnGetMathCoord;
- property OnGetRealCoord: TCoord2DFunc read FOnGetRealCoord write FOnGetRealCoord;
- property OnPaint: TNotifyEvent read FOnPaint write FOnPaint;
- //Inherited events to be published
- property OnEnter;
- property OnExit;
- property OnDblClick;
- property OnDragDrop;
- property OnDragOver;
- property OnEndDrag;
- property OnMouseDown;
- property OnMouseMove;
- property OnMouseUp;
- property OnStartDrag;
- end;
-
- const
- ValidOps = ['+','-','*','/',')','(','^','='];
-
- procedure Register;
- function V2D(x,y: double): TVector2D;
- function V3D(vx,vy,vz: double): TVector3D;
- function V2D2Point(V: TVector2D): TPoint;
- function V3DXY(V: TVector3D): TVector2D;
- function V3DXZ(V: TVector3D): TVector2D;
- function V3DZY(V: TVector3D): TVector2D;
- function V3D2STR(V: TVector3D): String;
-
- implementation
-
- //-----Global routines-----//
-
- function V3D(vx,vy,vz: double): TVector3D;
- begin
- with Result do
- begin
- X := vx;
- Y := vy;
- Z := vz;
- end;
- end;
-
- function V3DXY(V: TVector3D): TVector2D;
- begin
- with Result do
- begin
- x := V.x;
- y := V.y;
- end;
- end;
-
- function V3DXZ(V: TVector3D): TVector2D;
- begin
- with Result do
- begin
- x := V.x;
- y := V.z;
- end;
- end;
-
- function V3DZY(V: TVector3D): TVector2D;
- begin
- with Result do
- begin
- x := V.z;
- y := V.y;
- end;
- end;
-
- function V3D2STR(V: TVector3D): String;
- var
- SX,SY,SZ: String;
- begin
- Str(V.x,SX);
- Str(V.y,SY);
- Str(V.z,SZ);
- Result := '<'+SX+','+SY+','+SZ+'>';
- end;
-
- function V2D(x,y: double): TVector2D;
- var
- AResult: TVector2D;
- begin
- AResult.x := x;
- AResult.y := y;
- Result := AResult;
- end;
-
- function V2D2Point(V: TVector2D): TPoint;
- begin
- with Result do
- begin
- x := Round(V.x);
- y := Round(V.y);
- end;
- end;
-
- procedure Register;
- begin
- RegisterComponents('Custom', [TAxesView]);
- end;
-
- //-----TVarList implementation-----//
-
- function TVarList.GetIndex(AValue: String): Integer;
- var
- i: integer;
- begin
- if Count>0 then
- begin
- for i := 0 to Count-1 do
- begin
- if PVariableRecord(items[i])^.VarName = AValue then
- begin
- Result := i;
- Exit;
- end;
- end;
- end;
- //Nothing found--return -1 as error code
- Result := -1;
- end;
-
- function TVarList.GetVars(Index: String): PVariableRecord;
- var
- ti: integer;
- begin
- ti := GetIndex(Index);
- if ti > -1 then
- Result := PVariableRecord(Items[ti])
- else
- Result := nil;
- end;
-
- function TVarList.NewVar(AName: String; AData: Double): PVariableRecord;
- var
- VarRec: PVariableRecord;
- ti: integer;
- begin
- ti := GetIndex(AName);
- if ti = -1 then
- begin
- VarRec := New(PVariableRecord);
- VarRec^.VarName := AName;
- VarRec^.VarData := AData;
- Add(VarRec);
- end
- else
- begin
- VarRec := PVariableRecord(Items[ti]);
- VarRec^.VarData := AData;
- end;
- Result := VarRec;
- end;
-
- destructor TVarList.Destroy;
- begin
- while Count>0 do
- begin
- Dispose(Items[0]);
- Delete(0);
- end;
- inherited Destroy;
- end;
-
- //-----TExpressionNode implementation-----//
-
- constructor TExpressionNode.Create(AOwner: TExpressionNode;
- ATree: TExpressionTree);
- begin
- inherited Create;
- FParent := AOwner;
- FTree := ATree;
- FChildren := TList.Create;
- FValidValue := False;
- end;
-
- destructor TExpressionNode.Destroy;
- begin
- while FChildren.Count > 0 do
- begin
- TExpressionNode(FChildren.items[0]).Free;
- FChildren.Delete(0);
- end;
- FChildren.Free;
- inherited Destroy;
- end;
-
- function TExpressionNode.CreateChild: TExpressionNode;
- var
- tNode: TExpressionNode;
- begin
- tNode := TExpressionNode.Create(Self,Tree);
- Children.Add(tNode);
- Result := tNode;
- end;
-
- function TExpressionNode.Evaluate(SuppressErrors,UseVars: Boolean): Double;
- var
- tf: double;
- i: longint;
- invop: boolean;
- begin
- //Evaluate the children first
- if Children.Count>0 then
- begin
- for i := 0 to Children.Count-1 do
- TExpressionNode(Children.items[i]).Evaluate(SuppressErrors,UseVars);
- end;
- //Now that children are evaluated, do this node
- case TokenType of
- ttUnknown:
- begin
- //This hasn't been trimmed so just evaluate the first child
- if Children.Count > 0 then
- begin
- NumValue := TExpressionNode(Children.items[0]).
- Evaluate(SuppressErrors,UseVars);
- ValidValue := TExpressionNode(Children.items[0]).ValidValue;
- Result := NumValue;
- end
- else
- begin
- if not SuppressErrors then
- begin
- ShowMessage('Error in evaluation tree: ttUnknown node with no children.');
- Exit;
- end;
- end;
- end;
- ttConstant:
- begin
- if not ValidValue then
- begin
- //Store everything as floats for now
- try
- tf := StrToFloat(Value);
- //Ok, it's a valid float value so store it
- NumValue := tf;
- ValidValue := True;
- except
- //It's not a valid numeric constant!
- on EConvertError do
- begin
- if not SuppressErrors then
- begin
- ShowMessage('Invalid numeric constant: '+Value);
- Exit;
- end;
- end;
- end;
- end;
- Result := NumValue;
- end;
- ttVariable:
- begin
- if UseVars then
- begin
- //Make sure variable hasn't changed from token
- if not Assigned(VarPointer) or (VarPointer^.VarName <> Value) then
- begin
- //Find new variable or create one if needed
- VarPointer := Tree.VarList.Vars[Value];
- if VarPointer = nil then
- VarPointer := Tree.VarList.NewVar(Value,0);
- end;
- //Variable is now valid
- NumValue := VarPointer^.VarData;
- ValidValue := True;
- end
- else
- ValidValue := False;
- end;
- ttOperation:
- begin
- //This is where the math takes place
- ValidValue := false;
- invop := true;
- if Value = '+' then
- begin
- if Children.Count = 2 then
- begin
- NumValue := TExpressionNode(Children.items[0]).NumValue+
- TExpressionNode(Children.items[1]).NumValue;
- ValidValue := TExpressionNode(Children.items[0]).ValidValue and
- TExpressionNode(Children.items[1]).ValidValue;
- invop := false;
- end
- else
- begin
- ValidValue := False;
- if not SuppressErrors then
- begin
- ShowMessage('Error: addition operation with '+
- IntToStr(Children.Count)+' values.');
- Exit;
- end;
- end;
- end;
- if Value = '-' then
- begin
- if Children.Count = 2 then
- begin
- NumValue := TExpressionNode(Children.items[0]).NumValue-
- TExpressionNode(Children.items[1]).NumValue;
- ValidValue := TExpressionNode(Children.items[0]).ValidValue and
- TExpressionNode(Children.items[1]).ValidValue;
- invop := false;
- end
- else
- begin
- ValidValue := False;
- if not SuppressErrors then
- begin
- ShowMessage('Error: subtraction operation with '+
- IntToStr(Children.Count)+' values.');
- Exit;
- end;
- end;
- end;
- if Value = '*' then
- begin
- if Children.Count = 2 then
- begin
- NumValue := TExpressionNode(Children.items[0]).NumValue*
- TExpressionNode(Children.items[1]).NumValue;
- ValidValue := TExpressionNode(Children.items[0]).ValidValue and
- TExpressionNode(Children.items[1]).ValidValue;
- invop := false;
- end
- else
- begin
- ValidValue := False;
- if not SuppressErrors then
- begin
- ShowMessage('Error: multiplacation operation with '+
- IntToStr(Children.Count)+' values.');
- Exit;
- end;
- end;
- end;
- if Value = '/' then
- begin
- if Children.Count = 2 then
- begin
- NumValue := TExpressionNode(Children.items[0]).NumValue/
- TExpressionNode(Children.items[1]).NumValue;
- ValidValue := TExpressionNode(Children.items[0]).ValidValue and
- TExpressionNode(Children.items[1]).ValidValue;
- invop := false;
- end
- else
- begin
- ValidValue := False;
- if not SuppressErrors then
- begin
- ShowMessage('Error: division operation with '+
- IntToStr(Children.Count)+' values.');
- Exit;
- end;
- end;
- end;
- if Value = 'div' then
- begin
- if Children.Count = 2 then
- begin
- NumValue := Round(TExpressionNode(Children.items[0]).NumValue/
- TExpressionNode(Children.items[1]).NumValue);
- ValidValue := TExpressionNode(Children.items[0]).ValidValue and
- TExpressionNode(Children.items[1]).ValidValue;
- invop := false;
- end
- else
- begin
- ValidValue := False;
- if not SuppressErrors then
- begin
- ShowMessage('Error: integer division operation with '+
- IntToStr(Children.Count)+' values.');
- Exit;
- end;
- end;
- end;
- if Value = 'mod' then
- begin
- if Children.Count = 2 then
- begin
- NumValue := Round(TExpressionNode(Children.items[0]).NumValue) mod
- Round(TExpressionNode(Children.items[1]).NumValue);
- ValidValue := TExpressionNode(Children.items[0]).ValidValue and
- TExpressionNode(Children.items[1]).ValidValue;
- invop := false;
- end
- else
- begin
- ValidValue := False;
- if not SuppressErrors then
- begin
- ShowMessage('Error: modulo operation with '+
- IntToStr(Children.Count)+' values.');
- Exit;
- end;
- end;
- end;
- if Value = 'and' then
- begin
- if Children.Count = 2 then
- begin
- NumValue := Round(TExpressionNode(Children.items[0]).NumValue) and
- Round(TExpressionNode(Children.items[1]).NumValue);
- ValidValue := TExpressionNode(Children.items[0]).ValidValue and
- TExpressionNode(Children.items[1]).ValidValue;
- invop := false;
- end
- else
- begin
- ValidValue := False;
- if not SuppressErrors then
- begin
- ShowMessage('Error: and operation with '+
- IntToStr(Children.Count)+' values.');
- Exit;
- end;
- end;
- end;
- if Value = 'or' then
- begin
- if Children.Count = 2 then
- begin
- NumValue := Round(TExpressionNode(Children.items[0]).NumValue) or
- Round(TExpressionNode(Children.items[1]).NumValue);
- ValidValue := TExpressionNode(Children.items[0]).ValidValue and
- TExpressionNode(Children.items[1]).ValidValue;
- invop := false;
- end
- else
- begin
- ValidValue := False;
- if not SuppressErrors then
- begin
- ShowMessage('Error: or operation with '+
- IntToStr(Children.Count)+' values.');
- Exit;
- end;
- end;
- end;
- if Value = 'xor' then
- begin
- if Children.Count = 2 then
- begin
- NumValue := Round(TExpressionNode(Children.items[0]).NumValue) xor
- Round(TExpressionNode(Children.items[1]).NumValue);
- ValidValue := TExpressionNode(Children.items[0]).ValidValue and
- TExpressionNode(Children.items[1]).ValidValue;
- invop := false;
- end
- else
- begin
- ValidValue := False;
- if not SuppressErrors then
- begin
- ShowMessage('Error: addition operation with '+
- IntToStr(Children.Count)+' values.');
- Exit;
- end;
- end;
- end;
- if Value = '=' then
- begin
- if Children.Count = 2 then
- begin
- NumValue := Ord(TExpressionNode(Children.items[0]).NumValue=
- TExpressionNode(Children.items[1]).NumValue);
- ValidValue := TExpressionNode(Children.items[0]).ValidValue and
- TExpressionNode(Children.items[1]).ValidValue;
- invop := false;
- end
- else
- begin
- ValidValue := False;
- if not SuppressErrors then
- begin
- ShowMessage('Error: equative operation with '+
- IntToStr(Children.Count)+' values.');
- Exit;
- end;
- end;
- end;
- if Value = '^' then
- begin
- if Children.Count = 2 then
- begin
- NumValue := Exp(TExpressionNode(Children.items[1]).NumValue *
- Ln(TExpressionNode(Children.items[0]).NumValue));
- ValidValue := TExpressionNode(Children.items[0]).ValidValue and
- TExpressionNode(Children.items[1]).ValidValue;
- invop := false;
- end
- else
- begin
- ValidValue := False;
- if not SuppressErrors then
- begin
- ShowMessage('Error: exponential operation with '+
- IntToStr(Children.Count)+' values.');
- Exit;
- end;
- end;
- if invop then
- begin
- if not SuppressErrors then
- ShowMessage('Error: invalid operation '+Value);
- end
- end;
- end;
- end;
- Result := NumValue;
- end;
-
-
-
- //-----TExpressionTree implementation-----//
-
- constructor TExpressionTree.Create;
- begin
- inherited Create;
- FCheckSyntax := True;
- FTokens := TStringList.Create;
- end;
-
- destructor TExpressionTree.Destroy;
- begin
- if Assigned(FTopNode) then
- FTopNode.Free;
- FTokens.Free;
- inherited Destroy;
- end;
-
- procedure TExpressionTree.RemoveBadTokens;
- var
- i: longint;
- begin
- if Tokens.Count > 0 then
- begin
- //Cut out extra parentheses.
- i := 1;
- repeat
- if (Tokens[i-1] = '(') and (Tokens[i+1] = ')') then
- begin
- //No need for parentheses around a single token
- Tokens.Delete(i-1);
- Tokens.Delete(i);
- i := 1;
- end;
- Inc(i);
- until (i>(Tokens.Count-2))
- end;
- end;
-
- procedure TExpressionTree.MakeTokens;
- var
- incount,start: longint;
- begin
- Tokens.Clear;
- incount := 1;
- start := 1;
- repeat
- //Read new token until we find a valid operation char or a space
- while not (FExpression[incount] in ValidOps)
- and not (FExpression[incount] = ' ') do
- begin
- //It's still a valid token so keep reading
- Inc(incount);
- if (incount>Length(FExpression)) then
- begin
- //We've gone past the end of the string
- //Write the token and exit
- Tokens.Add(Copy(FExpression,start,incount-start));
- Exit;
- end;
- end;
- //We've found a valid operation or space character
- //Record token before operation or space
- if (incount-start)>0 then
- Tokens.Add(Copy(FExpression,start,incount-start));
- //Record an operation, but discard a space
- if FExpression[incount] in ValidOps then
- Tokens.Add(Copy(FExpression,incount,1));
- start := incount+1;
- incount := start;
- until (start>Length(FExpression));
- end;
-
- procedure TExpressionTree.SetExpression(AValue: String);
- var
- i,c: longint;
- CNode,tNode: TExpressionNode;
- CToken: String;
- tf: double;
-
- function IsLowerOp: Boolean;
- begin
- Result := (CToken='+') or (CToken='-') or (CToken='=') or (CToken='or') or
- (CToken='and') or (CToken='xor');
- end;
-
- function IsMiddleOp: Boolean;
- begin
- Result := (CToken='*') or (CToken='/') or (CToken='div') or (CToken='mod');
- end;
-
- begin
- if (AValue<>FExpression) and (AValue <> '') then
- begin
- if CheckSyntax then
- begin
- c := 0;
- for i := 1 to Length(AValue) do
- begin
- case AValue[i] of
- '(': Inc(c);
- ')': Dec(c);
- end;
- end;
- if (c<>0) then
- begin
- ShowMessage('Error in syntax: parentheses unbalanced at '+IntToStr(c));
- Exit;
- end;
- end;
- //No syntax errors found yet--set property and clear old tree
- FExpression := AValue;
- if Assigned(FTopNode) then
- FTopNode.Free;
- FTopNode := TExpressionNode.Create(nil,Self);
- //Parse expression so it's ready for tree generation
- MakeTokens;
- //Remove any tokens that are useless or will cause undesired behavior
- RemoveBadTokens;
- //Let's make the tree
- if Tokens.Count>0 then
- begin
- CNode := FTopNode;
- for i := 0 to Tokens.Count-1 do
- begin
- CToken := Tokens.strings[i];
- if CToken='(' then
- begin
- CNode := CNode.CreateChild;
- //Create temporary token for reference of order-of-operations code
- CNode.Value := '(';
- CNode.TokenType := ttOperation;
- CNode := CNode.CreateChild;
- Continue;
- end;
- if IsLowerOp or (Assigned(CNode.Parent) and (CNode.Parent.Value='^')
- and IsMiddleOp) then
- begin
- //Low priority or after an exponent--go up the tree
- if not Assigned(CNode.Parent) then
- begin
- ShowMessage('Error in expression: '+FExpression);
- Exit;
- end;
- repeat
- CNode := CNode.Parent;
- until not Assigned(CNode.Parent) or
- (CNode.TokenType<>ttOperation) or (CNode.Parent.Value = '(');
- if CNode.TokenType = ttOperation then
- begin
- //Create a new higher level node in-between CNode and CNode.Parent
- //If CNode.Parent is nil then we must setup a new TopNode field
- if not Assigned(CNode.Parent) then
- begin
- if not (CNode = FTopNode) then
- begin
- ShowMessage('Expression Evaluator Error: '+
- 'Node with nil parent is not top node!');
- Exit;
- end;
- FTopNode := TExpressionNode.Create(nil,Self);
- FTopNode.Children.Add(CNode);
- CNode.Parent := FTopNode;
- end
- else
- begin
- //Otherwise just insert the new node
- tNode := TExpressionNode.Create(CNode.Parent,Self);
- tNode.Children.Add(CNode);
- //Don't forget to reset children of Parent node
- CNode.Parent.Children.Remove(CNode);
- CNode.Parent.Children.Add(tNode);
- CNode.Parent := tNode;
- end;
- end
- else
- begin
- //There is no operation at this node currently--replace the node
- //data for this operation and continue loop
- CNode.TokenType := ttOperation;
- CNode.Value := CToken;
- Continue;
- end;
- //Node inserted--set current node to parent so that operation can
- //be inserted
- CNode := CNode.Parent;
- CNode.TokenType := ttOperation;
- CNode.Value := CToken;
- Continue;
- end;
- if IsMiddleOp or (CToken='^') then
- begin
- //Middle operation that isn't past an exponent or an exponent--go down
- if not Assigned(CNode.Parent) then
- begin
- ShowMessage('Error in expression: '+FExpression);
- Exit;
- end;
- if CNode.Parent.TokenType = ttOperation then
- begin
- //Insert node before CNode
- tNode := TExpressionNode.Create(CNode,Self);
- while (CNode.Children.Count>0) do
- begin
- TExpressionNode(CNode.Children.items[0]).Parent := tNode;
- tNode.Children.Add(CNode.Children.items[0]);
- CNode.Children.Delete(0);
- end;
- tNode.Value := CNode.Value;
- tNode.TokenType := CNode.TokenType;
- CNode.Children.Add(tNode);
- end
- else
- CNode := CNode.Parent;
- CNode.TokenType := ttOperation;
- CNode.Value := CToken;
- Continue;
- end;
- if CToken = ')' then
- begin
- //We need to backtrack through tree to find our origin--
- {while Assigned(CNode.Parent)
- and (CNode.Parent.TokenType<>ttUnknown) do CNode := CNode.Parent;}
- //Look for '(' marker
- repeat
- CNode := CNode.Parent;
- until (CNode.Parent.Value = '(');
- //Delete the marker and setup CNode
- tNode := CNode;
- CNode := tNode.Parent;
- CNode.Parent.Children.Remove(CNode);
- while (CNode.Children.Count>0) do
- begin
- TExpressionNode(CNode.Children.items[0]).Parent := CNode.Parent;
- CNode.Parent.Children.Add(CNode.Children.items[0]);
- CNode.Children.Delete(0);
- end;
- CNode.Free;
- CNode := tNode;
- //Check to see if TopNode is already assigned and if so insert a new
- //node above it--assuming we're at the TopNode
- if not Assigned(CNode.Parent) then
- begin
- if not (CNode = FTopNode) then
- begin
- ShowMessage('Expression Evaluator Error: '+
- 'Node with nil parent is not top node!');
- Exit;
- end;
- //Create new top node
- FTopNode := TExpressionNode.Create(nil,Self);
- FTopNode.Children.Add(CNode);
- CNode.Parent := FTopNode;
- end;
- Continue;
- end;
- //It's not a symbol or an operator, so it must be something else
- //Check to see if it's a valid numeric constant
- try
- tf := StrToFloat(CToken);
- //It's a valid constant since no exception has been raised
- //Create child node for constant
- CNode := CNode.CreateChild;
- CNode.TokenType := ttConstant;
- CNode.Value := CToken;
- except
- on EConvertError do
- begin
- //It's not a valid number, so let's assume it's a variable
- CNode := CNode.CreateChild;
- CNode.TokenType := ttVariable;
- CNode.Value := CToken;
- end;
- end;
- //Continue for loop through tokens
- end;
- end;
- end;
- end;
-
- //-----TAxesView implementation-----//
-
- procedure TAxesView.DrawGrid;
- var
- i: longint;
- CVector: TVector2D;
- begin
- with Canvas do
- begin
- Pen.Color := GridColor;
- Pen.Width := 1;
- for i := 0 to FGrid.Count-1 do
- begin
- CVector := FGrid.Data[i];
- PenPos := Point(0,Round(CVector.y));
- LineTo(Width-1,Round(CVector.y));
- PenPos := Point(Round(CVector.x),0);
- LineTo(Round(CVector.x),Height-1);
- end;
- end;
- end;
-
- procedure TAxesView.DrawAxes;
- begin
- with Canvas do
- begin
- Pen.Width := 2;
- Pen.Color := XAxisColor;
- PenPos := (Point(0,Round(Origin.y)));
- LineTo(Width-1,Round(Origin.y));
- Pen.Color := YAxisColor;
- PenPos := (Point(Round(Origin.x),0));
- LineTo(Round(Origin.x),Height-1);
- end;
- end;
-
- procedure TAxesView.DrawLabels;
- var
- i: longint;
- begin
- Canvas.Brush.Style := bsClear;
- if Labels.Count > 0 then
- begin
- for i := 0 to Labels.Count-1 do
- begin
- with TDataLabel(Labels.items[i]) do
- Canvas.TextOut(x,y,Data);
- end;
- end;
- end;
-
- procedure TAxesView.Paint;
- begin
- //Draw grid
- if ShowGrid then DrawGrid;
- //Draw axes
- if ShowAxes then DrawAxes;
- //Draw labels
- if ShowLabels then DrawLabels;
- if Assigned(FOnPaint) then
- FOnPaint(Self);
- end;
-
- procedure TAxesView.RecalcScale;
- var
- diffx,diffy,i,k,tv: longint;
- j: double;
- CXMod,CYMod: Integer;
- DL: TDataLabel;
- ts,tsb: string;
- CVector: TVector2D;
-
- function max(a,b: longint): longint;
- begin
- if (a>b) then
- Result := a
- else
- Result := b;
- end;
-
- begin
- FRXScale := Width/(XMax-XMin);
- FRYScale := Height/(YMin-YMax);
- if ShowAxes or ShowGrid or ShowLabels then FOrigin := GetRealCoord(V2D(0,0));
- if ShowGrid then
- begin
- //Prepare for maximum possible allocation
- FGrid.Count :=
- abs(Round((XMax-XMin)/XScale))+abs(Round((YMax-YMin)/YScale))*2;
- diffx := abs(Round(GetRealCoord(V2D(XScale,0)).x-Origin.x));
- diffy := abs(Round(GetRealCoord(V2D(0,YScale)).y-Origin.y));
- j := 0;
- //Do -XY first
- i := Round(Origin.x);
- k := Round(Origin.y);
- repeat
- i := i - diffx;
- k := k - diffy;
- FGrid.Data[Round(j)] := V2D(i,k);
- j := j + 1;
- until (i<0)and(k<0);
- //Do +XY next
- i := Round(Origin.x);
- k := Round(Origin.y);
- repeat
- i := i + diffx;
- k := k + diffy;
- FGrid.Data[Round(j)] := V2D(i,k);
- j := j + 1;
- until (i>=Width) and (k>=Height);
- FGrid.Count := Round(j);
- end;
- if ShowLabels then
- begin
- //Free old information from Labels list
- while (Labels.Count > 0) do
- begin
- TDataLabel(Labels.items[0]).Free;
- Labels.Delete(0);
- end;
- //Setup variables for scale including spacing
- CVector := GetRealCoord(V2D(XMin+XScale,YMax-YScale));
- Str(XMin:0:Decimals,ts);
- Str(XMax:0:Decimals,tsb);
- CXMod := Trunc(max(Canvas.TextWidth(ts),
- Canvas.TextWidth(tsb))*1.05/(CVector.x))+1;
- Str(YMin:0:Decimals,ts);
- Str(YMax:0:Decimals,tsb);
- CYMod := Trunc(max(Canvas.TextHeight(ts),
- Canvas.TextHeight(tsb))*1.05/(CVector.y))+1;
- //Store the actual labels
- //Do X- first
- j := -(CXMod*XScale);
- repeat
- DL := TDataLabel.Create;
- Str(j:0:Decimals,ts);
- DL.Data := ts;
- tv := Round(GetRealCoord(V2D(j,0)).x);
- DL.x := tv-(Canvas.TextWidth(ts) div 2);
- if (AlignLabelX = aaNegative) then
- DL.y := Round(Origin.y) + 1
- else
- DL.y := Round(Origin.y)-Canvas.TextHeight(ts)-1;
- Labels.Add(DL);
- j := j - (CXMod*XScale);
- until (j < XMin);
- //Do X+ now
- j := (CXMod*XScale);
- repeat
- DL := TDataLabel.Create;
- Str(j:0:Decimals,ts);
- DL.Data := ts;
- tv := Round(GetRealCoord(V2D(j,0)).x);
- DL.x := tv-(Canvas.TextWidth(ts) div 2);
- if (AlignLabelX = aaNegative) then
- DL.y := Round(Origin.y) + 1
- else
- DL.y := Round(Origin.y)-Canvas.TextHeight(ts)-1;
- Labels.Add(DL);
- j := j + (CXMod*XScale);
- until (j > XMax);
- //Do Y- now
- j := -(CYMod*YScale);
- repeat
- DL := TDataLabel.Create;
- Str(j:0:Decimals,ts);
- DL.Data := ts;
- tv := Round(GetRealCoord(V2D(0,j)).y);
- if (AlignLabelY = aaNegative) then
- DL.x := Round(Origin.X)-Canvas.TextWidth(ts)-1
- else
- DL.x := Round(Origin.X)+1;
- DL.y := tv-(Canvas.TextHeight(ts) div 2);
- Labels.Add(DL);
- j := j - (CYMod*YScale);
- until (j < YMin);
- //Do Y+ now
- j := (CYMod*YScale);
- repeat
- DL := TDataLabel.Create;
- Str(j:0:Decimals,ts);
- DL.Data := ts;
- tv := Round(GetRealCoord(V2D(0,j)).y);
- if (AlignLabelY = aaNegative) then
- DL.x := Round(Origin.X)-Canvas.TextWidth(ts)-1
- else
- DL.x := Round(Origin.X)+1;
- DL.y := tv-(Canvas.TextHeight(ts) div 2);
- Labels.Add(DL);
- j := j + (CYMod*YScale);
- until (j > YMax);
- end;
- end;
-
- procedure TAxesView.DoAutoPan(X,Y: Integer);
- var
- V: TVector2D;
- begin
- V := GetMathCoord(V2D(X,Y));
- if (X<5) then FXMin := FXMin-FXScale
- else
- begin
- if (X>Width-5) then FXMax := FXMax+FXScale
- else
- begin
- if (Y<5) then FYMax := FYMax+FYScale
- else
- begin
- if (Y>Height-5) then FYMin := FYMin-FYScale
- else
- Exit;
- end;
- end;
- end;
- RecalcScale;
- V := GetRealCoord(V);
- SetCursorPos(ClientOrigin.X+Round(V.X),ClientOrigin.Y+Round(V.Y));
- end;
-
- function TAxesView.GetRealCoord(InV: TVector2D): TVector2D;
- begin
- if Assigned(FOnGetRealCoord) then
- FOnGetRealCoord(InV);
- with Result do
- begin
- x := RXScale*(InV.x-XMin);
- y := RYScale*(InV.y-YMax);
- end;
- end;
-
- function TAxesView.GetMathCoord(Inv: TVector2D): TVector2D;
- begin
- with InV do
- begin
- x := (x/RXScale)+XMin;
- y := (y/RYScale)+YMax;
- end;
- if Assigned(OnGetMathCoord) then
- FOnGetMathCoord(InV);
- Result := InV;
- end;
-
- procedure TAxesView.Zoom(Percent: Integer);
- begin
- if Percent>0 then
- begin
- FXMin := FXMin*(Percent/100);
- FXMax := FXMax*(Percent/100);
- FYMin := FYMin*(Percent/100);
- FYMax := FYMax*(Percent/100);
- RecalcScale;
- if (csDesigning in ComponentState) or AutoUpdate then Refresh;
- end;
- end;
-
- procedure TAxesView.SetScale(AXMin,AXMax,AXScale,AYMin,AYMax,AYScale: Double);
- begin
- FXMin := AXMin;
- FXMax := AXMax;
- FXScale := AXScale;
- FYMin := AYMin;
- FYMax := AYMax;
- FYScale := AYScale;
- RecalcScale;
- if (csDesigning in ComponentState) or AutoUpdate then Refresh;
- end;
-
- constructor TAxesView.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- FGrid := TDataSet2D.Create(20);
- //Setup default values
- FXMin := -100;
- FXMax := 100;
- FXScale := 10;
- FYMin := -100;
- FYMax := 100;
- FYScale := 10;
- FDecimals := 0;
- FShowGrid := True;
- FShowAxes := True;
- FShowLabels := True;
- FLabels := TList.Create;
- FXAxisColor := clBlack;
- FYAxisColor := clBlack;
- FGridColor := clSilver;
- FAutoUpdate := True;
- Color := clWhite;
- Width := 200;
- Height := 200;
- //Hook events in object
- Font.OnChange := FontChange;
- end;
-
- destructor TAxesView.Destroy;
- begin
- FGrid.Free;
- inherited Destroy;
- end;
-
- procedure TAxesView.WMSize(var Message: TWMSize);
- begin
- if (Message.Width < 50) or (Message.Height < 50) then Exit;
- inherited;
- if not (csLoading in ComponentState) then
- begin
- RecalcScale;
- Refresh;
- end;
- end;
-
- procedure TAxesView.FontChange(Sender: TObject);
- begin
- Canvas.Font.Assign(Font);
- if (csDesigning in ComponentState) or AutoUpdate then
- begin
- RecalcScale;
- Refresh;
- end;
- end;
-
- procedure TAxesView.SetDecimals(AValue: Integer);
- begin
- if (AValue<>FDecimals) and (AValue>=0) and (AValue<9) then
- begin
- FDecimals := AValue;
- if (csDesigning in ComponentState) or AutoUpdate then
- begin
- RecalcScale;
- Refresh;
- end;
- end;
- end;
-
- procedure TAxesView.SetXMin(AValue: Double);
- begin
- if (AValue <> FXMin) and (AValue < FXMax) then
- begin
- FXMin := AValue;
- if (csDesigning in ComponentState) or AutoUpdate then
- begin
- RecalcScale;
- Refresh;
- end;
- end;
- end;
-
- procedure TAxesView.SetXMax(AValue: Double);
- begin
- if (AValue <> FXMax) and (AValue > FXMin) then
- begin
- FXMax := AValue;
- if (csDesigning in ComponentState) or AutoUpdate then
- begin
- RecalcScale;
- Refresh;
- end;
- end;
- end;
-
- procedure TAxesView.SetXScale(AValue: Double);
- begin
- if AValue <> FXScale then
- begin
- FXScale := AValue;
- if (csDesigning in ComponentState) or AutoUpdate then
- begin
- RecalcScale;
- Refresh;
- end;
- end;
- end;
-
- procedure TAxesView.SetYMin(AValue: Double);
- begin
- if (AValue <> FYMin) and (AValue<FYMax) then
- begin
- FYMin := AValue;
- if (csDesigning in ComponentState) or AutoUpdate then
- begin
- RecalcScale;
- Refresh;
- end;
- end;
- end;
-
- procedure TAxesView.SetYMax(AValue: Double);
- begin
- if (AValue <> FYMax) and (AValue>FYMin) then
- begin
- FYMax := AValue;
- if (csDesigning in ComponentState) or AutoUpdate then
- begin
- RecalcScale;
- Refresh;
- end;
- end;
- end;
-
- procedure TAxesView.SetYScale(AValue: Double);
- begin
- if AValue <> FYScale then
- begin
- FYScale := AValue;
- if (csDesigning in ComponentState) or AutoUpdate then
- begin
- RecalcScale;
- Refresh;
- end;
- end;
- end;
-
- procedure TAxesView.SetShowGrid(AValue: Boolean);
- begin
- if AValue <> FShowGrid then
- begin
- FShowGrid := AValue;
- if (csDesigning in ComponentState) or AutoUpdate then
- begin
- RecalcScale;
- Refresh;
- end;
- end;
- end;
-
- procedure TAxesView.SetShowAxes(AValue: Boolean);
- begin
- if AValue <> FShowAxes then
- begin
- FShowAxes := AValue;
- if (csDesigning in ComponentState) or AutoUpdate then
- begin
- RecalcScale;
- Refresh;
- end;
- end;
- end;
-
- procedure TAxesView.SetAlignLabelX(AValue: TAxesAlign);
- begin
- if AValue<>FAlignLabelX then
- begin
- FAlignLabelX := AValue;
- if (csDesigning in ComponentState) or AutoUpdate then
- begin
- RecalcScale;
- Refresh;
- end;
- end;
- end;
-
- procedure TAxesView.SetAlignLabelY(AValue: TAxesAlign);
- begin
- if AValue<>FAlignLabelY then
- begin
- FAlignLabelY := AValue;
- if (csDesigning in ComponentState) or AutoUpdate then
- begin
- RecalcScale;
- Refresh;
- end;
- end;
- end;
-
- procedure TAxesView.SetShowLabels(AValue: Boolean);
- begin
- if AValue <> FShowLabels then
- begin
- FShowLabels := AValue;
- if (csDesigning in ComponentState) or AutoUpdate then Refresh;
- end;
- end;
-
- procedure TAxesView.SetXAxisColor(AValue: TColor);
- begin
- if AValue <> FXAxisColor then
- begin
- FXAxisColor := AValue;
- if (csDesigning in ComponentState) or AutoUpdate then Refresh;
- end;
- end;
-
- procedure TAxesView.SetYAxisColor(AValue: TColor);
- begin
- if AValue <> FYAxisColor then
- begin
- FYAxisColor := AValue;
- if (csDesigning in ComponentState) or AutoUpdate then Refresh;
- end;
- end;
-
- procedure TAxesView.SetGridColor(AValue: TColor);
- begin
- if AValue <> FGridColor then
- begin
- FGridColor := AValue;
- if (csDesigning in ComponentState) or AutoUpdate then Refresh;
- end;
- end;
-
- //-----TDataSet2D implementation-----//
-
- procedure TDataSet2D.SetData(Index: longint; AValue: TVector2D);
- begin
- with TVector2D(Ptr(Longint(FData)+(Index*SizeOf(TVector2D)))^) do
- begin
- x := AValue.x;
- y := AValue.y;
- end;
- end;
-
- function TDataSet2D.GetData(Index: longint): TVector2D;
- begin
- with TVector2D(Ptr(Longint(FData)+(Index*SizeOf(TVector2D)))^) do
- begin
- Result.x := x;
- Result.y := y;
- end;
- end;
-
- procedure TDataSet2D.SetCount(AValue: longint);
- begin
- if AValue <> FCount then
- begin
- FCount := AValue;
- ReAllocMem(FData,AValue*SizeOf(TVector2D));
- end;
- end;
-
- constructor TDataSet2D.Create(ACount: longint);
- begin
- inherited Create;
- FCount := ACount;
- GetMem(FData,ACount*SizeOf(TVector2D));
- end;
-
- destructor TDataSet2D.Destroy;
- begin
- FreeMem(FData,FCount*SizeOf(TVector2D));
- inherited Destroy;
- end;
-
- //-----TDataSet3D implementation-----//
-
- procedure TDataSet3D.SetData(Index: longint; AValue: TVector3D);
- begin
- with TVector3D(Ptr(Longint(FData)+(Index*SizeOf(TVector3D)))^) do
- begin
- x := AValue.x;
- y := AValue.y;
- z := AValue.z;
- end;
- end;
-
- function TDataSet3D.GetData(Index: longint): TVector3D;
- begin
- with TVector3D(Ptr(Longint(FData)+(Index*SizeOf(TVector3D)))^) do
- begin
- Result.x := x;
- Result.y := y;
- Result.z := z;
- end;
- end;
-
- procedure TDataSet3D.SetCount(AValue: longint);
- begin
- if AValue <> FCount then
- begin
- FCount := AValue;
- ReAllocMem(FData,AValue*SizeOf(TVector3D));
- end;
- end;
-
- constructor TDataSet3D.Create(ACount: longint);
- begin
- inherited Create;
- FCount := ACount;
- GetMem(FData,ACount*SizeOf(TVector3D));
- end;
-
- destructor TDataSet3D.Destroy;
- begin
- FreeMem(FData,FCount*SizeOf(TVector3D));
- inherited Destroy;
- end;
-
- end.
-