{ ******************************************************************************** YaPeTaVi - Yet another Periodic Table Viewer Copyright (C) 2011 Geoffray Levasseur . All rights reserved. http://www.geoffray-levasseur.org/ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ******************************************************************************** Description: Component displaying electronic distribution and associated tools } unit uElecDistClass; {$mode objfpc} interface uses Classes, SysUtils, ExtCtrls, Controls; const ElecDiam = 11; type TElecDist = array [1..7] of Byte; TElectronicCloud = array[1..7, 1..32] of TShape; TElecDistDisplay = class(TComponent) private FElecDist: TElecDist; FElecConf: string; FAtomShape: TShape; FLayer1Shape: TShape; FLayer2Shape: TShape; FLayer3Shape: TShape; FLayer4Shape: TShape; FLayer5Shape: TShape; FLayer6Shape: TShape; FLayer7Shape: TShape; FElectrons: TElectronicCloud; FExpandedElecConf: string; FOwner: TWinControl; procedure SetElecConf(AConf: string); procedure GetElecCoord(Layer, Num, Total: Integer; out Top, Left: Integer); procedure CreateElectron(var Shape: TShape; Top, Left: integer); public constructor Create(AOwner: TWinControl); virtual; property ElecronicConf: string read FElecConf write SetElecConf; property ExpandedElecConf: string read FExpandedElecConf; end; function ExpandElecConf(S: string): string; function ConvElecConfToDist(Conf: string; out Dist: TElecDist): Boolean; implementation uses uConst, uDebug, Graphics; //This function is not generic... It only work with the syntax used in YaPeTaVi function ExpandElecConf(S: string): string; const ExtHe = '1s2'; ExtNe = ExtHe + ' 2s2 2p6'; ExtAr = ExtNe + ' 3s2 3p6'; ExtKr = ExtAr + ' 3d10 4s2 4p6'; ExtXe = ExtKr + ' 4d10 5s2 5p6'; ExtRn = ExtXe + ' 4f14 5d10 6s2 6p6'; var Tmp: string; begin PrintLnDbg('Expanding ' + S + '...', vlHigh); if (S[1] <> '[') and (S[4] <> ']') then begin if S[1] <> '1' then //the first layer must be 1s Result := '' else Result := S; Exit; end else begin if Pos('He', S) = 2 then Result := ExtHe + ' ' + Trim(Copy(S, 5, Length(S) - 4)) else if Pos('Ne', S) = 2 then Result := ExtNe + ' ' + Trim(Copy(S, 5, Length(S) - 4)) else if Pos('Ar', S) = 2 then Result := ExtAr + ' ' + Trim(Copy(S, 5, Length(S) - 4)) else if Pos('Kr', S) = 2 then Result := ExtKr + ' ' + Trim(Copy(S, 5, Length(S) - 4)) else if Pos('Xe', S) = 2 then Result := ExtXe + ' ' + Trim(Copy(S, 5, Length(S) - 4)) else if Pos('Rn', S) = 2 then Result := ExtRn + ' ' + Trim(Copy(S, 5, Length(S) - 4)) else begin Result := ''; //trigger a more explicit error (syntax) PrintLnDbg('Exited ExpandElecConf with syntax error!', vlHigh); end; end; end; function ConvElecConfToDist(Conf: string; out Dist: TElecDist): Boolean; var I: Integer; Layer: Integer; procedure InitDist; var J: Integer; begin for J := 1 to 7 do Dist[J] := 0; end; begin I := 1; InitDist; Conf := ExpandElecConf(Conf); Result := True; if Conf = '' then begin PrintLnDbg('Error while expanding ElecConf... Aborting!', vlLow); Result := False; Exit; end; while I <= Length(Conf) do begin if Conf[I] <> ' ' then if Conf[I] in ['1'..'7'] then begin Layer := StrToInt(Conf[I]); Inc(I, 2); //skip the letter as we don't need it for the use we have if Conf[I] in ['0'..'9'] then begin if Conf[I + 1] in ['0'..'9'] then begin Dist[Layer] := Dist[Layer] + StrToInt(Copy(Conf, I, 2)); Inc(I, 2); end else begin Dist[Layer] := Dist[Layer] + StrToInt(Conf[I]); Inc(I); end; end else begin PrintLnDbg('Malformed electronic configuration "' + Conf + '" (second check)', vlLow); InitDist; Result := False; Exit; end; end else begin PrintLnDbg('Malformed electronic configuration "' + Conf + '" (first check)', vlLow); InitDist; Result := False; Exit; end else Inc(I); end; end; constructor TElecDistDisplay.Create(AOwner: TWinControl); var Max: Integer; begin FOwner := AOwner; FElecConf := ''; FExpandedElecConf := ''; //make it square taking the lowest lenth of the owner if AOwner.Width > AOwner.Height then Max := AOwner.Height - 45 else Max := AOwner.Width - 40; FLayer7Shape := TShape.Create(AOwner); FLayer7Shape.Shape := stCircle; FLayer7Shape.Color := clBlack; FLayer7Shape.Pen.Style := psSolid; FLayer7Shape.Brush.Style := bsClear; if AOwner.Width > AOwner.Height then begin FLayer7Shape.Left := (AOwner.Width - Max) div 2; FLayer7Shape.Width := Max; FLayer7Shape.Top := 30; FLayer7Shape.Height := Max; end else begin FLayer7Shape.Top := (AOwner.Height - Max) div 2; FLayer7Shape.Height := Max; FLayer7Shape.Left := 25; FLayer7Shape.Width := Max; end; FLayer7Shape.Parent := AOwner; FLayer7Shape.Visible := True; FLayer6Shape := TShape.Create(AOwner); FLayer6Shape.Shape := stCircle; FLayer6Shape.Color := clBlack; FLayer6Shape.Pen.Style := psSolid; FLayer6Shape.Brush.Style := bsClear; FLayer6Shape.Top := FLayer7Shape.Top + 17; FLayer6Shape.Width := FLayer7Shape.Width - 34; FLayer6Shape.Left := FLayer7Shape.Left + 17; FLayer6Shape.Height := FLayer7Shape.Height - 34; FLayer6Shape.Parent := AOwner; FLayer6Shape.Visible := True; FLayer5Shape := TShape.Create(AOwner); FLayer5Shape.Shape := stCircle; FLayer5Shape.Color := clBlack; FLayer5Shape.Pen.Style := psSolid; FLayer5Shape.Brush.Style := bsClear; FLayer5Shape.Top := FLayer6Shape.Top + 17; FLayer5Shape.Width := FLayer6Shape.Width - 34; FLayer5Shape.Left := FLayer6Shape.Left + 17; FLayer5Shape.Height := FLayer6Shape.Height - 34; FLayer5Shape.Parent := AOwner; FLayer5Shape.Visible := True; FLayer4Shape := TShape.Create(AOwner); FLayer4Shape.Shape := stCircle; FLayer4Shape.Color := clBlack; FLayer4Shape.Pen.Style := psSolid; FLayer4Shape.Brush.Style := bsClear; FLayer4Shape.Top := FLayer5Shape.Top + 17; FLayer4Shape.Width := FLayer5Shape.Width - 34; FLayer4Shape.Left := FLayer5Shape.Left + 17; FLayer4Shape.Height := FLayer5Shape.Height - 34; FLayer4Shape.Parent := AOwner; FLayer4Shape.Visible := True; FLayer3Shape := TShape.Create(AOwner); FLayer3Shape.Shape := stCircle; FLayer3Shape.Color := clBlack; FLayer3Shape.Pen.Style := psSolid; FLayer3Shape.Brush.Style := bsClear; FLayer3Shape.Top := FLayer4Shape.Top + 17; FLayer3Shape.Width := FLayer4Shape.Width - 34; FLayer3Shape.Left := FLayer4Shape.Left + 17; FLayer3Shape.Height := FLayer4Shape.Height - 34; FLayer3Shape.Parent := AOwner; FLayer3Shape.Visible := True; FLayer2Shape := TShape.Create(AOwner); FLayer2Shape.Shape := stCircle; FLayer2Shape.Color := clBlack; FLayer2Shape.Pen.Style := psSolid; FLayer2Shape.Brush.Style := bsClear; FLayer2Shape.Top := FLayer3Shape.Top + 17; FLayer2Shape.Width := FLayer3Shape.Width - 34; FLayer2Shape.Left := FLayer3Shape.Left + 17; FLayer2Shape.Height := FLayer3Shape.Height - 34; FLayer2Shape.Parent := AOwner; FLayer2Shape.Visible := True; FLayer1Shape := TShape.Create(AOwner); FLayer1Shape.Shape := stCircle; FLayer1Shape.Color := clBlack; FLayer1Shape.Pen.Style := psSolid; FLayer1Shape.Brush.Style := bsClear; FLayer1Shape.Top := FLayer2Shape.Top + 17; FLayer1Shape.Width := FLayer2Shape.Width - 34; FLayer1Shape.Left := FLayer2Shape.Left + 17; FLayer1Shape.Height := FLayer2Shape.Height - 34; FLayer1Shape.Parent := AOwner; FLayer1Shape.Visible := True; FAtomShape := TShape.Create(AOwner); FAtomShape.Shape := stCircle; FAtomShape.Color := clMaroon; FAtomShape.Pen.Style := psSolid; FAtomShape.Pen.Color := clMaroon; FAtomShape.Brush.Style := bsSolid; FAtomShape.Brush.Color := clMaroon; FAtomShape.Top := FLayer1Shape.Top + 20; FAtomShape.Width := FLayer1Shape.Width - 40; FAtomShape.Left := FLayer1Shape.Left + 20; FAtomShape.Height := FLayer1Shape.Height - 40; FAtomShape.Parent := AOwner; FAtomShape.Visible := True; end; procedure TElecDistDisplay.CreateElectron(var Shape: TShape; Top, Left: integer); begin if Shape = nil then Shape := TShape.Create(FOwner); Shape.Top := Top; Shape.Left := Left; Shape.Width := ElecDiam; Shape.Height := ElecDiam; Shape.Color := clBlue; Shape.Shape := stCircle; Shape.Brush.Color := clBlue; Shape.Brush.Style := bsSolid; Shape.Pen.Color := clBlue; Shape.Pen.Style := psSolid; Shape.Visible := True; Shape.Parent := FOwner; end; procedure TElecDistDisplay.GetElecCoord(Layer, Num, Total: integer; out Top, Left: integer); var Teta: Real; Shape: TShape; begin case Layer of 1: Shape := FLayer1Shape; 2: Shape := FLayer2Shape; 3: Shape := FLayer3Shape; 4: Shape := FLayer4Shape; 5: Shape := FLayer5Shape; 6: Shape := FLayer6Shape; 7: Shape := FLayer7Shape; end; Teta := (Pi / 2) + (2 * Pi * Num) / Total; //calc angle Top := Round((Shape.Width / 2) * - Sin(Teta)) + Shape.Top + (Shape.Height div 2) - (ElecDiam div 2); Left := Round((Shape.Width / 2) * Cos(Teta)) + Shape.Left + (Shape.Width div 2) - (ElecDiam div 2); end; procedure TElecDistDisplay.SetElecConf(AConf: string); var Layer, I: integer; EX, EY: integer; LayerShape: TShape; begin for Layer := 1 to 7 do for I := 1 to 32 do begin FElectrons[Layer, I].Free; FElectrons[Layer, I] := nil; end; FElecConf := AConf; FExpandedElecConf := ExpandElecConf(AConf); if not ConvElecConfToDist(AConf, FElecDist) then FElecConf := ''; //Draw it PrintDbg('Distribution is ', vlHigh); for I := 1 to 7 do PrintDbgResult(IntToStr(FElecDist[I]) + ' ', vlHigh); PrintLnDbg(vlHigh); for Layer := 1 to 7 do begin case Layer of 1: LayerShape := FLayer1Shape; 2: LayerShape := FLayer2Shape; 3: LayerShape := FLayer3Shape; 4: LayerShape := FLayer4Shape; 5: LayerShape := FLayer5Shape; 6: LayerShape := FLayer6Shape; 7: LayerShape := FLayer7Shape; end; LayerShape.Visible := FElecDist[Layer] > 0; if FElecDist[Layer] > 0 then for I := 1 to FElecDist[Layer] do begin GetElecCoord(Layer, I, FElecDist[Layer], EX, EY); PrintLnDbg('Showing electron ' + IntToStr(I) + ' on layer ' + IntToStr(Layer) + ' at coord ' + IntToStr(EX) + ', ' + IntToStr(EY), vlHigh); CreateElectron(FElectrons[Layer, I], EX, EY); end; end; end; end.