Rubik's cube avec firemonkey 3d

Description

Le but de ce programme a été pour moi de travailler avec la partie 3D de Firemonkey et d'une certaine manière promouvoir mon outil preferé.

A/ But et limites

Le Rubik's Cube est fonctionnel mais ne dispose pas la fonctionnalité resolution automatique.
Ceci a deja été fait et ne presente plus de fort interet.

Ceux qui s'attendent a un respect de la notation officielle des faces du Rubik's cube seront déçus.
Comment reperer la face AVANT apres des rotations des plans X Y ou Z.
J'ai donc utilisé la couleur du cube pivot (central à chaque face) pour decrire un plan.
Ceci est important pour la lecture du source. Mais aussi pour le jeu au clavier et avec les boutons colorés.
Par contre pour le jeu à la souris no souci !

les cubes coins sont sensibles au clic souris pour les rotations de face. Un tirer/Lacher sur la zone
grise permet une rotation de tout le cube.

Z- plan Rouge (avant au depart)
Z+ plan Orange (arriere au depart)
X+ plan Bleu (Droit au depart)
X- plan Vert (Gauche au depart)
Y- plan Blanc (Haut au depart)
Y+ plan Jaune (Bas au depart)

Source / Exemple :


unit UMain;

interface
//               ______             __
//              /\  ___\           /\ \__
//  _____       \ \ \__/ ___    ___\ \ ,_\    __    ___      __     __     __  __
// /\ '__`\      \ \  _\/ __`\/' _ `\ \ \/  /'__`\/' _ `\  /'__`\ /'__`\  /\ \/\ \
// \ \ \_\ \   __ \ \ \/\ \_\ \\ \/\ \ \ \_/\  __//\ \/\ \/\  __//\ \_\ \_\ \ \_\ \
//  \ \ ,__/ / \_\ \ \_\ \____/ \_\ \_\ \__\ \____\ \_\ \_\ \____\ \__/ \_\\ \____/
//   \ \ \/  \/_/  \/_/\/___/ \/_/\/_/\/__/\/____/\/_/\/_/\/____/\/__/\/_/ \/___/
//    \ \_\
//     \/_/     P2F - 2012 - Rubik'sCube
//     FREEWARE - Aucune diffusion  commerciale basée sur ce code source
//     n'est autorisée sans autorisation préalable de l'auteur.
//     Rubik's Cube 3D Pascal Fonteneau
//     Interfaces- Fun Parts - Pascal Fonteneau and Whiler

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.Types3D, FMX.Objects3D,
  FMX.Ani, FMX.Layouts, FMX.Memo, System.Math, System.StrUtils, FMX.Effects, FMX.Platform,
  FMX.Objects, FMX.ListBox, FMX.Colors;

const
  FUN_LABELS : array[0..11] of string     = ('Damier', 'Barre', '2 Cubes',
                                             '3 Cubes', 'Carré', 'Damier coloré',
                                             'Plus', 'Bande', 'Diagonale',
                                             'Angle', 'L', 'Tetris');
  FUN_VALUES : array[0..11] of AnsiString =
   ('OORRWWYYGGBB', 'RRGGOORRGGor', 'WWGGRRwOOYBrBrBryOOw',
                                             'GGyRRyowoRByWBOGGOWW', 'gBrOYwgB', 'OORRWWYYGGBBgBrOYwgFB',
                                             'RGGBBRROOWWGGBBRROOYYr', 'rGGRRbyRRWRRBBRRYRRwbRRGG', 'GBROGBROGBRO',
                                             'WGGRbGoRRBgYWWrGGw', 'OBBYRbGyoRyWgRRw', 'GBROGBROGBROWWYY');

  ANIMATION : array[0..10] of String      = ('Linear','Quadratic','Cubic','Quartic','Quintic','Sinusoidal',
                                             'Exponential','Circular','Elastic','Back','Bounce');

type
  TStickPosition = (PoFront, PoBack, PoTop, PoBottom, PoRight, PoLeft, PoNone);
  TDirection     = (DiNone, DiLeft, DiRight, DiTop, DiBottom);

  TFormMain = class(TForm)
    LabelX: TLabel;
    LabelY: TLabel;
    LabelZ: TLabel;
    StyleBook1: TStyleBook;
    Viewport3D2: TViewport3D;
    CubeCentral: TCube;
    RedPivot: TCube;
    GreenPivot: TCube;
    WhitePivot: TCube;
    OrangePivot: TCube;
    BluePivot: TCube;
    YellowPivot: TCube;
    BtnRedLeft: TButton;
    BtnRedRight: TButton;
    BtnGreenLeft: TButton;
    BtnGreenRight: TButton;
    BtnWhiteLeft: TButton;
    BtnWhiteRight: TButton;
    BtnOrangeLeft: TButton;
    BtnOrangeRight: TButton;
    BtnBlueLeft: TButton;
    BtnBlueRight: TButton;
    BtnYellowLeft: TButton;
    BtnYellowRight: TButton;
    FaTurnRed: TFloatAnimation;
    FaTurnGreen: TFloatAnimation;
    FaTurnWhite: TFloatAnimation;
    FaTurnOrange: TFloatAnimation;
    FaTurnYellow: TFloatAnimation;
    Dummy: TDummy;
    Camera: TCamera;
    GbRotation: TGroupBox;
    ArcDialX: TArcDial;
    ArcDialY: TArcDial;
    ArcDialZ: TArcDial;
    FaTurnBlue: TFloatAnimation;
    CC1: TCube;
    CC2: TCube;
    CC5: TCube;
    CC3: TCube;
    CC4: TCube;
    CC6: TCube;
    CC8: TCube;
    CC7: TCube;
    CA1: TCube;
    CA2: TCube;
    CA3: TCube;
    CA4: TCube;
    CA5: TCube;
    CA6: TCube;
    CA7: TCube;
    CA8: TCube;
    CA9: TCube;
    CA10: TCube;
    CA11: TCube;
    CA12: TCube;
    PLCC1R: TPlane;
    PLCA1R: TPlane;
    PLCA2R: TPlane;
    PLCA3R: TPlane;
    PLCA4R: TPlane;
    PLCC2R: TPlane;
    PLCC3R: TPlane;
    PLCC4R: TPlane;
    PLCC5W: TPlane;
    PLCA9W: TPlane;
    PLCC6W: TPlane;
    PLCA5W: TPlane;
    PLCA6W: TPlane;
    PLCC1W: TPlane;
    PLCA1W: TPlane;
    PLCC2W: TPlane;
    PLCC2B: TPlane;
    PLCA6B: TPlane;
    PLCC6B: TPlane;
    PLCA3B: TPlane;
    PLCA11B: TPlane;
    PLCC3B: TPlane;
    PLCA7B: TPlane;
    PLCC7B: TPlane;
    PLCC5V: TPlane;
    PLCA5V: TPlane;
    PLCC1V: TPlane;
    PLCA10V: TPlane;
    PLCA2V: TPlane;
    PLCC8V: TPlane;
    PLCA8V: TPlane;
    PLCC4V: TPlane;
    PLCC4J: TPlane;
    PLCA4J: TPlane;
    PLCC3J: TPlane;
    PLCA8J: TPlane;
    PLCA7J: TPlane;
    PLCC8J: TPlane;
    PLCA12J: TPlane;
    PLCC7J: TPlane;
    PLCC6O: TPlane;
    PLCA9O: TPlane;
    PLCC5O: TPlane;
    PLCA11O: TPlane;
    PLCA10O: TPlane;
    PLCC7O: TPlane;
    PLCA12O: TPlane;
    PLCC8O: TPlane;
    GbSize: TGroupBox;
    BtnStart: TButton;
    GBFun: TGroupBox;
    LbInfoLink: TLabel;
    ShadowEffectP2F: TShadowEffect;
    cbFun: TComboBox;
    sclytCommand: TScaledLayout;
    recCommand: TRectangle;
    pbFun: TProgressBar;
    GbAnimation: TGroupBox;
    CbAnimation: TComboBox;
    tbSpeedAnim: TTrackBar;
    CPnlSpeedAnimHint: TCalloutPanel;
    txtSpeedAnimHint: TText;
    FaSpeedAnimHint: TFloatAnimation;
    TbSize: TTrackBar;
    CbRotateX: TCheckBox;
    CbRotateY: TCheckBox;
    CbRotateZ: TCheckBox;
    FaAutoRotateZ: TFloatAnimation;
    FaAutoRotateY: TFloatAnimation;
    FaAutoRotateX: TFloatAnimation;
    LbSpeedAnimation: TLabel;
    TbSpeedRotation: TTrackBar;
    CPnlSpeedRotatHint: TCalloutPanel;
    TxtSpeedRotateHint: TText;
    FaSpeedRotateHint: TFloatAnimation;
    LbSpeedRotate: TLabel;
    CpnlCubeSize: TCalloutPanel;
    TxtCubeSize: TText;
    FaCubeSize: TFloatAnimation;
    MainLight: TLight;
    GbLight: TGroupBox;
    RbAmbiance: TRadioButton;
    RbSpotLight: TRadioButton;
    BwTbLight: TBWTrackBar;
    procedure ColorBtnClick(Sender: TObject);
    procedure RotationFinish(Sender: TObject);
    procedure ArcDialXChange(Sender: TObject);
    procedure BtnStartClick(Sender: TObject);
    procedure FormKeyDown(Sender: TObject; var Key: Word; var KeyChar: Char; Shift: TShiftState);
    procedure StickCornerMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Single; RayPos, RayDir: TVector3D);
    procedure StickMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single; RayPos, RayDir: TVector3D);
    procedure FormCreate(Sender: TObject);
    procedure LbInfoLinkClick(Sender: TObject);
    procedure Viewport3D2MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
    procedure Viewport3D2MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Single);
    procedure Viewport3D2MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
    procedure StickCornerMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single;
      RayPos, RayDir: TVector3D);
    procedure cbFunChange(Sender: TObject);
    procedure RotatePlan(Pos1, Pos2: Single; TheDirection, Di1, Di2, Di3, Di4: TDirection;
      P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12: AnsiChar);
    procedure CbAnimationChange(Sender: TObject);
    procedure tbSpeedAnimChange(Sender: TObject);
    procedure TbSizeChange(Sender: TObject);
    procedure CbRotateZChange(Sender: TObject);
    procedure CbRotateXChange(Sender: TObject);
    procedure CbRotateYChange(Sender: TObject);
    procedure TbSpeedRotationChange(Sender: TObject);
    procedure TbHorizontalChange(Sender: TObject);
    procedure RbAmbianceChange(Sender: TObject);
    procedure BwTbLightChange(Sender: TObject);

  private
    { Déclarations privées }
    LastDirection: ShortInt;  // Sens de la dernière rotation  90 Aig -90 Inv
    Direction: TDirection;    // Sens du Mvt de la souris
    ClickX: Single;           // Emplacement du clic X initial
    ClickY: Single;           // Emplacement du clic Y initial
    ClickXR: Single;          // Emplacement du clic X Radial
    ClickYR: Single;          // Emplacement du clic Y Radial

    MvtOn: Boolean;           // Vaut Vrai lors d'un clic sur un stick
    MvtAuto: Boolean;         // Vaut Vrai lors des déplacements programmés
    procedure Rotate(ThePivotAnimation: TFloatAnimation; StartValue, StepValue: Integer); overload;
    procedure Rotate(Value: AnsiChar); overload;
    procedure TurnXPlan(TheCube: TCube);
    procedure TurnYPlan(TheCube: TCube);
    procedure TurnZPlan(TheCube: TCube);
    Function  GetStickPosition(TheStick: TPlane): TStickPosition;
    procedure MoveStick(TheStick: TPlane; Px, Py, Pz, Rx, Ry, Rz: Single);
    procedure PlayMvt(TheMvts: AnsiString);
    procedure SortRubik;
    procedure SortCubes;
    procedure SortCube(TheCube: TCube; Px, Py, Pz: Single);
    procedure SortSticks;
    procedure SetLight;
    // function Translate(s: string): string;
  public
    { Déclarations publiques }

  end;

var
  FormMain: TFormMain;

implementation

{$R *.fmx}

uses uWxPlatform;

procedure TFormMain.FormCreate(Sender: TObject);
var
  iLoop: Integer;
begin
  MvtOn   := False;   // pas de mouvement de la souris en cours
  MvtAuto := False;   // pas de mouvement automatique en cours

  // Load the arrays
  for iLoop := Low(FUN_LABELS) to High(FUN_LABELS) do
    cbFun.Items.Add(FUN_LABELS[iLoop] + ' (' + IntToStr(Length(FUN_VALUES[iLoop])) + ')');

  for iLoop := Low(ANIMATION) to High(ANIMATION) do
    CbAnimation.Items.Add(ANIMATION[iLoop]);

  CbAnimation.ItemIndex := 0;
  CbAnimation.OnChange(Self);

  tbSpeedAnim.Value     := 1.6;
  TbSpeedRotation.Value := 11;
  TbSize.Value          := 39;
end;

/// *************************************************
/// Rubik's cube camera zoom +/-
/// *************************************************

procedure TFormMain.TbSizeChange(Sender: TObject);
begin
  FaCubeSize.Stop;
  CpnlCubeSize.Opacity := 1;
  TxtCubeSize.Text     := Format('%2.0f', [TbSize.Value + 10]);
  Camera.Position.Z    := TbSize.Value - 60;
  FaCubeSize.Start;
end;

// Rotation speed
procedure TFormMain.tbSpeedAnimChange(Sender: TObject);
var
  speed: Single;
begin
  FaSpeedAnimHint.Stop;
  CPnlSpeedAnimHint.Opacity := 1;
  speed                     := 2.1 - tbSpeedAnim.Value;
  txtSpeedAnimHint.Text     := Format('%2.1f s', [speed]);
  FaTurnRed.Duration        := speed;
  FaTurnGreen.Duration      := speed;
  FaTurnWhite.Duration      := speed;
  FaTurnOrange.Duration     := speed;
  FaTurnYellow.Duration     := speed;
  FaTurnBlue.Duration       := speed;
  FaSpeedAnimHint.Start;
end;

// ****************************************
// Automatic speed rotation of the cube
// ****************************************

procedure TFormMain.TbSpeedRotationChange(Sender: TObject);
begin
  FaSpeedRotateHint.Stop;
  CPnlSpeedRotatHint.Opacity := 1;
  TxtSpeedRotateHint.Text    := Format('%2.1f s', [Abs(TbSpeedRotation.Value - 22)]);
  FaAutoRotateX.Duration     := 22 - TbSpeedRotation.Value;
  FaAutoRotateY.Duration     := 22 - TbSpeedRotation.Value;
  FaAutoRotateZ.Duration     := 22 - TbSpeedRotation.Value;
  FaSpeedRotateHint.Start;
end;

procedure TFormMain.TbHorizontalChange(Sender: TObject);
begin

end;

// ******************************************************
// Lors du changement de l'animation
// ******************************************************
procedure TFormMain.CbAnimationChange(Sender: TObject);
begin
  FaTurnRed.Interpolation    := TInterpolationType(CbAnimation.ItemIndex);
  FaTurnGreen.Interpolation  := TInterpolationType(CbAnimation.ItemIndex);
  FaTurnWhite.Interpolation  := TInterpolationType(CbAnimation.ItemIndex);
  FaTurnOrange.Interpolation := TInterpolationType(CbAnimation.ItemIndex);
  FaTurnYellow.Interpolation := TInterpolationType(CbAnimation.ItemIndex);
  FaTurnBlue.Interpolation   := TInterpolationType(CbAnimation.ItemIndex);
end;

/// *************************************************
/// Common procedure for the click on a color button
/// *************************************************
procedure TFormMain.ColorBtnClick(Sender: TObject);
begin
  cbFun.ItemIndex := -1;
  Rotate(AnsiChar((Sender as TButton).Text[1]));
end;

/// *************************************************
/// Procedure de lancement de rotation au clavier
/// *************************************************
procedure TFormMain.FormKeyDown(Sender: TObject; var Key: Word; var KeyChar: Char; Shift: TShiftState);
begin
  Rotate(AnsiChar(KeyChar));
end;

/// *************************************************
/// Lance un appel au bon FloatAnimation et boucle en
/// Attendant la fin de l'animation
/// *************************************************
procedure TFormMain.Rotate(Value: AnsiChar);
begin
 case Value of
    'r': Rotate(FaTurnRed, 360, 270);
    'R': Rotate(FaTurnRed, 0, 90);
    'o': Rotate(FaTurnOrange, 0, 90);
    'O': Rotate(FaTurnOrange, 360, 270);
    'y': Rotate(FaTurnYellow, 0, 90);
    'Y': Rotate(FaTurnYellow, 360, 270);
    'b': Rotate(FaTurnBlue, 0, 90);
    'B': Rotate(FaTurnBlue, 360, 270);
    'g': Rotate(FaTurnGreen, 360, 270);
    'G': Rotate(FaTurnGreen, 0, 90);
    'w': Rotate(FaTurnWhite, 360, 270);
    'W': Rotate(FaTurnWhite, 0, 90);
 end;
end;

/// *****************************************************
/// Rechercher les cubes Coins et Arretes situés sur le
/// même plan que le cube allant pivoter et en faire ses
/// enfants.
/// Puis, lancement de l'animation ROTATION
/// *****************************************************
procedure TFormMain.Rotate(ThePivotAnimation: TFloatAnimation; StartValue, StepValue: Integer);
var
  I: Integer;
begin
  recCommand.Enabled := False; // desactiver le panneau de commandes

  // récuperation du sens de la rotation
  LastDirection := 90;      // sens des aiguilles d'une montre pour 90 degres
  if StartValue = 360 then
    LastDirection := -90;   // sens trigo

  // Lier les cubes Coins et Arretes au cube allant pivoter
  for I := 0 to FormMain.ComponentCount - 1 do
  begin
    if (FormMain.Components[I] Is TCube) then
    begin
      if ((FormMain.Components[I] As TCube).Tag = 2) or ((FormMain.Components[I] As TCube).Tag = 3) then
      begin
        // Red plan
        if (SameValue((FormMain.Components[I] As TCube).Position.Z, -3, 0.1)) and (ThePivotAnimation = FaTurnRed) then
        begin
          (FormMain.Components[I] As TCube).Parent     := RedPivot;
          (FormMain.Components[I] As TCube).Position.Z := 0;
        end;
        // Orange plan
        if (SameValue((FormMain.Components[I] As TCube).Position.Z, 3, 0.1)) and (ThePivotAnimation = FaTurnOrange) then
        begin
          (FormMain.Components[I] As TCube).Parent     := OrangePivot;
          (FormMain.Components[I] As TCube).Position.Z := 0;
        end;
        // Green plan
        if (SameValue((FormMain.Components[I] As TCube).Position.X, -3, 0.1)) and (ThePivotAnimation = FaTurnGreen) then
        begin
          (FormMain.Components[I] As TCube).Parent     := GreenPivot;
          (FormMain.Components[I] As TCube).Position.X := 0;
        end;
        // Blue plan
        if (SameValue((FormMain.Components[I] As TCube).Position.X, 3, 0.1)) and (ThePivotAnimation = FaTurnBlue) then
        begin
          (FormMain.Components[I] As TCube).Parent     := BluePivot;
          (FormMain.Components[I] As TCube).Position.X := 0;
        end;
        // White plan
        if (SameValue((FormMain.Components[I] As TCube).Position.Y, -3, 0.1)) and (ThePivotAnimation = FaTurnWhite) then
        begin
          (FormMain.Components[I] As TCube).Parent     := WhitePivot;
          (FormMain.Components[I] As TCube).Position.Y := 0;
        end;
        // Yellow plan
        if (SameValue((FormMain.Components[I] As TCube).Position.Y, 3, 0.1)) and (ThePivotAnimation = FaTurnYellow) then
        begin
          (FormMain.Components[I] As TCube).Parent     := YellowPivot;
          (FormMain.Components[I] As TCube).Position.Y := 0;
        end;
      end; // si cube Coin ou cuble arrete
    end; // si un cube
  end; // du for

  // Valuer et lancer l'animation
  ThePivotAnimation.StartValue := StartValue;
  ThePivotAnimation.StopValue  := StepValue;
  ThePivotAnimation.Start;
  while ThePivotAnimation.Running do Application.ProcessMessages;
end;

/// *************************************************
/// En fin d'animation ROTATION le cube ayant pivoté
// rend au cube central les enfants
/// *************************************************
procedure TFormMain.RotationFinish(Sender: TObject);
Var
  I: Integer;
  TmpX, TmpY, TmpZ: Single;
  ThePlan: TCube;
begin
  for I := 0 to FormMain.ComponentCount - 1 do
  begin
    if (FormMain.Components[I] Is TCube) then
    begin
      if ((FormMain.Components[I] As TCube).Tag = 2) or ((FormMain.Components[I] As TCube).Tag = 3) then
      begin
        if (FormMain.Components[I] As TCube).Parent <> CubeCentral then
        begin
          TmpX    := (FormMain.Components[I] As TCube).AbsolutePosition.X;
          TmpY    := (FormMain.Components[I] As TCube).AbsolutePosition.Y;
          TmpZ    := (FormMain.Components[I] As TCube).AbsolutePosition.Z;
          ThePlan := TCube((FormMain.Components[I] As TCube).Parent);
          (FormMain.Components[I] As TCube).Position.X := TmpX;
          (FormMain.Components[I] As TCube).Position.Y := TmpY;
          (FormMain.Components[I] As TCube).Position.Z := TmpZ;
          (FormMain.Components[I] As TCube).Parent     := CubeCentral;

          // plan Rouge (plan Z)
          if ThePlan = RedPivot then
          begin
            (FormMain.Components[I] As TCube).Position.Z := -3;
            TurnZPlan((FormMain.Components[I] As TCube));
          end;

          // plan Orange (Plan Z)
          if ThePlan = OrangePivot then
          begin
            (FormMain.Components[I] As TCube).Position.Z := 3;
            TurnZPlan((FormMain.Components[I] As TCube));
          end;

          // plan bleu (Plan X)
          if ThePlan = BluePivot then
          begin
            (FormMain.Components[I] As TCube).Position.X := 3;
            TurnXPlan((FormMain.Components[I] As TCube));
          end;

          // plan Vert (Plan X)
          if ThePlan = GreenPivot then
          begin
            (FormMain.Components[I] As TCube).Position.X := -3;
            TurnXPlan((FormMain.Components[I] As TCube));
          end;

          // plan Blanc (Plan Y)
          if ThePlan = WhitePivot then
          begin
            (FormMain.Components[I] As TCube).Position.Y := -3;
            TurnYPlan((FormMain.Components[I] As TCube));
          end;

          // plan Jaune  (Plan Y)
          if ThePlan = YellowPivot then
          begin
            (FormMain.Components[I] As TCube).Position.Y := 3;
            TurnYPlan((FormMain.Components[I] As TCube));
          end;
        end;
      end;
    end;
    if not MvtAuto then
      recCommand.Enabled := True; // le panneau de commande redevient actif
  end;
end;

/// *************************************************
/// Renvoi la position (le plan ) d'un stick
/// *************************************************
Function TFormMain.GetStickPosition(TheStick: TPlane): TStickPosition;
begin
  Result := PoNone;
  if SameValue(TheStick.Position.Y, -1.51, 0.1) then
    Result := PoTop;
  if SameValue(TheStick.Position.Y, 1.51, 0.1) then
    Result := PoBottom;
  if SameValue(TheStick.Position.X, -1.51, 0.1) then
    Result := PoLeft;
  if SameValue(TheStick.Position.X, 1.51, 0.1) then
    Result := PoRight;
  if SameValue(TheStick.Position.Z, 1.51, 0.1) then
    Result := PoBack;
  if SameValue(TheStick.Position.Z, -1.51, 0.1) then
    Result := PoFront;
end;

/// *************************************************
/// Place (colle)  un stick sur un plan des 6 plans
/// du cube
/// ************************************************
procedure TFormMain.MoveStick(TheStick: TPlane; Px, Py, Pz, Rx, Ry, Rz: Single);
begin
  TheStick.ResetRotationAngle; // Remise à zero des angles de rotation  IMPORTANT
  TheStick.Position.X      := Px;
  TheStick.Position.Y      := Py;
  TheStick.Position.Z      := Pz;
  TheStick.RotationAngle.X := Rx;
  TheStick.RotationAngle.Y := Ry;
  TheStick.RotationAngle.Z := Rz;
end;

/// **********************************************
/// Pour le plan Z ( Rouge et Orange)
/// En fonction du dernier sens de rotation
/// replace les sticks sur la bonne facette
/// **********************************************
procedure TFormMain.TurnZPlan(TheCube: TCube);
var
  I: Integer;
begin
  for I := 0 to TheCube.ChildrenCount - 1 do
  begin
    if LastDirection = 90 then // sens des aiguilles
    begin
      if GetStickPosition((TheCube.Children[I] AS TPlane)) = PoTop then
        MoveStick((TheCube.Children[I] AS TPlane), 1.51, 0, 0, 0, -90, 0) // Right
      else if GetStickPosition((TheCube.Children[I] AS TPlane)) = PoLeft then
        MoveStick((TheCube.Children[I] AS TPlane), 0, -1.51, 0, -90, 0, 0) // Up
      else if GetStickPosition((TheCube.Children[I] AS TPlane)) = PoBottom then
        MoveStick((TheCube.Children[I] AS TPlane), -1.51, 0, 0, 0, 90, 0) // Left
      else if GetStickPosition((TheCube.Children[I] AS TPlane)) = PoRight then
        MoveStick((TheCube.Children[I] AS TPlane), 0, 1.51, 0, 90, 0, 0); // Down

    end
    else
    begin
      if GetStickPosition((TheCube.Children[I] AS TPlane)) = PoTop then
        MoveStick((TheCube.Children[I] AS TPlane), -1.51, 0, 0, 0, 90, 0) // Left
      else if GetStickPosition((TheCube.Children[I] AS TPlane)) = PoLeft then
        MoveStick((TheCube.Children[I] AS TPlane), 0, 1.51, 0, 90, 0, 0) // Down
      else if GetStickPosition((TheCube.Children[I] AS TPlane)) = PoBottom then
        MoveStick((TheCube.Children[I] AS TPlane), 1.51, 0, 0, 0, -90, 0) // Right
      else if GetStickPosition((TheCube.Children[I] AS TPlane)) = PoRight then
        MoveStick((TheCube.Children[I] AS TPlane), 0, -1.51, 0, -90, 0, 0); // Up
    end;
  end;
end;

/// / **********************************************
/// Idem, pour le plan X ( Bleu et vert)
/// / **********************************************
procedure TFormMain.TurnXPlan(TheCube: TCube);
var
  I: Integer;
begin
  for I := 0 to TheCube.ChildrenCount - 1 do
  begin
    if LastDirection = 90 then // sens INVERSE des aiguilles
    begin
      if GetStickPosition((TheCube.Children[I] AS TPlane)) = PoTop then
        MoveStick((TheCube.Children[I] AS TPlane), 0, 0, -1.51, 0, 0, 0) // Front
      else if GetStickPosition((TheCube.Children[I] AS TPlane)) = PoBack then
        MoveStick((TheCube.Children[I] AS TPlane), 0, -1.51, 0, -90, 0, 0) // Up
      else if GetStickPosition((TheCube.Children[I] AS TPlane)) = PoBottom then
        MoveStick((TheCube.Children[I] AS TPlane), 0, 0, 1.51, 180, 0, 0) // Back
      else if GetStickPosition((TheCube.Children[I] AS TPlane)) = PoFront then
        MoveStick((TheCube.Children[I] AS TPlane), 0, 1.51, 0, 90, 0, 0); // Down
    end
    else
    begin
      if GetStickPosition((TheCube.Children[I] AS TPlane)) = PoTop then
        MoveStick((TheCube.Children[I] AS TPlane), 0, 0, 1.51, 180, 0, 0) // Back
      else if GetStickPosition((TheCube.Children[I] AS TPlane)) = PoBack then
        MoveStick((TheCube.Children[I] AS TPlane), 0, 1.51, 0, 90, 0, 0) // Down
      else if GetStickPosition((TheCube.Children[I] AS TPlane)) = PoBottom then
        MoveStick((TheCube.Children[I] AS TPlane), 0, 0, -1.51, 0, 0, 0) // Front
      else if GetStickPosition((TheCube.Children[I] AS TPlane)) = PoFront then
        MoveStick((TheCube.Children[I] AS TPlane), 0, -1.51, 0, -90, 0, 0); // Up
    end;
  end;
end;

/// **********************************************
/// Idem, pour le plan Y ( Blanc et Jaune)
/// **********************************************
procedure TFormMain.TurnYPlan(TheCube: TCube);
var
  I: Integer;
begin
  for I := 0 to TheCube.ChildrenCount - 1 do
  begin
    if LastDirection = 90 then // sens des aiguilles
    begin
      if GetStickPosition((TheCube.Children[I] AS TPlane)) = PoFront then
        MoveStick((TheCube.Children[I] AS TPlane), -1.51, 0, 0, 0, 90, 0) // Left
      else if GetStickPosition((TheCube.Children[I] AS TPlane)) = PoLeft then
        MoveStick((TheCube.Children[I] AS TPlane), 0, 0, 1.51, 180, 0, 0) // Back
      else if GetStickPosition((TheCube.Children[I] AS TPlane)) = PoBack then
        MoveStick((TheCube.Children[I] AS TPlane), 1.51, 0, 0, 0, -90, 0) // Right
      else if GetStickPosition((TheCube.Children[I] AS TPlane)) = PoRight then
        MoveStick((TheCube.Children[I] AS TPlane), 0, 0, -1.51, 0, 0, 0); // Front

    end
    else
    begin
      if GetStickPosition((TheCube.Children[I] AS TPlane)) = PoFront then
        MoveStick((TheCube.Children[I] AS TPlane), 1.51, 0, 0, 0, -90, 0) // Right
      else if GetStickPosition((TheCube.Children[I] AS TPlane)) = PoLeft then
        MoveStick((TheCube.Children[I] AS TPlane), 0, 0, -1.51, 0, 0, 0) // Front
      else if GetStickPosition((TheCube.Children[I] AS TPlane)) = PoBack then
        MoveStick((TheCube.Children[I] AS TPlane), -1.51, 0, 0, 0, 90, 0) // Left
      else if GetStickPosition((TheCube.Children[I] AS TPlane)) = PoRight then
        MoveStick((TheCube.Children[I] AS TPlane), 0, 0, 1.51, 180, 0, 0); // Back
    end;
  end;
end;

{$REGION 'Jeu à la souris'}

/// **********************************************
/// en cliquant sur un stick on arme la recherche
/// d'un mouvement
/// **********************************************
procedure TFormMain.StickMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single;
  RayPos, RayDir: TVector3D);
begin
  if MvtAuto then
    Exit;
  ClickXR := RayDir.X; // emplacement du clic en X  Radial
  ClickYR := RayDir.Y; // emplacement du clic en Y  Radial
  MvtOn   := True;
end;

/// **********************************************
/// En cliquant sur le fond on arme la rotation
/// du cube
/// **********************************************
procedure TFormMain.Viewport3D2MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
begin
  ClickX := X; // emplacement du clic en X
  ClickY := Y; // emplacement du clic en Y
end;

/// **********************************************
/// le deplacement de la souris fait pivoter le cube
/// **********************************************
procedure TFormMain.Viewport3D2MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Single);
begin
  IF MvtOn then
    Exit;
  if ssLeft in Shift then // si la souris bouge avec le clic droit enfoncé
  begin
    Direction := DiNone;
    IF (ClickX - X) > 3 then
      Direction := DiLeft;
    IF (X - ClickX) > 3 then
      Direction := DiRight;
    IF (ClickY - Y) > 3 then
      Direction := DiTop;
    IF (Y - ClickY) > 3 then
      Direction := DiBottom;

    if Direction <> DiNone then // une direction a été trouvée
    begin
      ClickX := X;
      ClickY := Y;
      if Direction = DiBottom then
        ArcDialX.Value := ArcDialX.Value - 5;
      if Direction = DiTop then
        ArcDialX.Value := ArcDialX.Value + 5;
      if Direction = DiLeft then
        ArcDialY.Value := ArcDialY.Value - 5;
      if Direction = DiRight then
        ArcDialY.Value := ArcDialY.Value + 5;
      ArcDialXChange(Self);
    end;
  end;
end;

/// **********************************************
/// Desarmement du mouvement
/// **********************************************
procedure TFormMain.Viewport3D2MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
begin
  MvtOn := False;
end;

procedure TFormMain.StickCornerMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single;
  RayPos, RayDir: TVector3D);
begin
  MvtOn := False;
end;

procedure TFormMain.StickCornerMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Single; RayPos, RayDir: TVector3D);
var
  PosXCube, PosYCube, PosZCube: int64;
begin
  if not MvtOn then
    Exit; // si pas de mouvement d'armé

  if ssLeft in Shift then // si la souris bouge avec le clic droit enfoncé
  begin
    Direction := DiNone;
    IF (ClickXR - RayDir.X) > 0.02 then
      Direction := DiLeft;
    IF (RayDir.X - ClickXR) > 0.02 then
      Direction := DiRight;
    IF (ClickYR - RayDir.Y) > 0.02 then
      Direction := DiTop;
    IF (RayDir.Y - ClickYR) > 0.02 then
      Direction := DiBottom;

    if Direction <> DiNone then // une direction a été trouvée
    begin
      cbFun.ItemIndex := -1;
      // recup de la position XYU du cube sous le stick
      PosXCube := Round(((Sender as TPlane).Parent as TCube).Position.X);
      PosYCube := Round(((Sender as TPlane).Parent as TCube).Position.Y);
      PosZCube := Round(((Sender as TPlane).Parent as TCube).Position.Z);
      // desarment du mouvement et reinitialisation de l'emplacement de la souris
      MvtOn   := False;
      ClickXR := RayDir.X;
      ClickYR := RayDir.Y;
      if (PosZCube = -3) and (SameValue((Sender as TPlane).Position.Z, -1.51, 0.1)) then // Plan Avant Rouge
        RotatePlan(PosXCube, PosYCube, Direction, DiTop, DiBottom, DiRight, DiLeft, 'g', 'G', 'w', 'W', 'Y', 'y', 'B', 'b', 'w',
          'W', 'Y', 'y')
      else if (PosZCube = 3) and (SameValue((Sender as TPlane).Position.Z, 1.51, 0.1)) then // Plan Arriere Orange
        RotatePlan(PosXCube, PosYCube, Direction, DiTop, DiBottom, DiRight, DiLeft, 'g', 'G', 'W', 'w', 'y', 'Y', 'B', 'b', 'W',
          'w', 'y', 'Y')
      else if (PosYCube = -3) and (SameValue((Sender as TPlane).Position.Y, -1.51, 0.1)) then // Plan Haut Blanc
        RotatePlan(PosXCube, PosZCube, Direction, DiTop, DiBottom, DiRight, DiLeft, 'g', 'G', 'R', 'r', 'o', 'O', 'B', 'b', 'R',
          'r', 'o', 'O')
      else if (PosYCube = 3) and (SameValue((Sender as TPlane).Position.Y, 1.51, 0.1)) then // Plan Bas Jaune
        RotatePlan(PosXCube, PosZCube, Direction, DiTop, DiBottom, DiRight, DiLeft, 'g', 'G', 'r', 'R', 'O', 'o', 'B', 'b', 'r',
          'R', 'O', 'o')
      else if (PosXCube = -3) and (SameValue((Sender as TPlane).Position.X, -1.51, 0.1)) then // Plan Gauche Vert
        RotatePlan(PosYCube, PosZCube, Direction, DiRight, DiLeft, DiBottom, DiTop, 'w', 'W', 'r', 'R', 'O', 'o', 'Y', 'y', 'r',
          'R', 'O', 'o')
      else if (PosXCube = 3) and (SameValue((Sender as TPlane).Position.X, 1.51, 0.1)) then // Plan Droit Bleu
        RotatePlan(PosYCube, PosZCube, Direction, DiRight, DiLeft, DiBottom, DiTop, 'w', 'W', 'R', 'r', 'o', 'O', 'Y', 'y', 'R',
          'r', 'o', 'O');
    end;
  end;
end;

procedure TFormMain.RotatePlan(Pos1, Pos2: Single; TheDirection, Di1, Di2, Di3, Di4: TDirection;
                               P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12: AnsiChar);
begin
  if Pos1 = -3 then
  begin
    if (TheDirection = Di1) then
      Rotate(P1);
    if (TheDirection = Di2) then
      Rotate(P2);
    if Pos2 = -3 then
    begin
      if (TheDirection = Di3) then
        Rotate(P3);
      if (TheDirection = Di4) then
        Rotate(P4);
    end
    else
    begin
      if (TheDirection = Di3) then
        Rotate(P5);
      if (TheDirection = Di4) then
        Rotate(P6);
    end;
  end
  else
  begin // coté bas
    if (TheDirection = Di1) then
      Rotate(P7);
    if (TheDirection = Di2) then
      Rotate(P8);
    if Pos2 = -3 then
    begin
      if (TheDirection = Di3) then
        Rotate(P9);
      if (TheDirection = Di4) then
        Rotate(P10);
    end
    else
    begin
      if (TheDirection = Di3) then
        Rotate(P11);
      if (TheDirection = Di4) then
        Rotate(P12);
    end;
  end;
end;

{$ENDREGION}
{$REGION 'Sort the  cube (not solving)'}

procedure TFormMain.BtnStartClick(Sender: TObject);
begin
  if MessageDlg('Restore initial cube?', TMsgDlgType.mtInformation, [TMsgDlgBtn.mbYes, TMsgDlgBtn.mbNo], 0) = MrNo then
    Exit;
  SortRubik();
  CbRotateX.IsChecked := False;
  CbRotateY.IsChecked := False;
  CbRotateZ.IsChecked := False;
  ArcDialX.Value      := -35;
  ArcDialY.Value      := -45;
  ArcDialZ.Value      := -18;
  cbFun.ItemIndex     := -1;
  ArcDialXChange(Self);
end;

procedure TFormMain.SortRubik();
begin
  SortCubes();
  SortSticks();
end;

procedure TFormMain.SortSticks();
begin
  // face Rouge
  MoveStick( PLCC1R,     0,     0, -1.51,   0,   0, 0);
  MoveStick( PLCC2R,     0,     0, -1.51,   0,   0, 0);
  MoveStick( PLCC4R,     0,     0, -1.51,   0,   0, 0);
  MoveStick( PLCC3R,     0,     0, -1.51,   0,   0, 0);
  MoveStick( PLCA1R,     0,     0, -1.51,   0,   0, 0);
  MoveStick( PLCA2R,     0,     0, -1.51,   0,   0, 0);
  MoveStick( PLCA3R,     0,     0, -1.51,   0,   0, 0);
  MoveStick( PLCA4R,     0,     0, -1.51,   0,   0, 0);
  // Face Bleu
  MoveStick( PLCC2B,  1.51,     0,     0,   0, -90, 0);
  MoveStick( PLCC6B,  1.51,     0,     0,   0, -90, 0);
  MoveStick( PLCC3B,  1.51,     0,     0,   0, -90, 0);
  MoveStick( PLCC7B,  1.51,     0,     0,   0, -90, 0);
  MoveStick( PLCA6B,  1.51,     0,     0,   0, -90, 0);
  MoveStick( PLCA3B,  1.51,     0,     0,   0, -90, 0);
  MoveStick(PLCA11B,  1.51,     0,     0,   0, -90, 0);
  MoveStick( PLCA7B,  1.51,     0,     0,   0, -90, 0);
  // Face Verte
  MoveStick( PLCC5V, -1.51,     0,     0,   0,  90, 0);
  MoveStick( PLCC1V, -1.51,     0,     0,   0,  90, 0);
  MoveStick( PLCC8V, -1.51,     0,     0,   0,  90, 0);
  MoveStick( PLCC4V, -1.51,     0,     0,   0,  90, 0);
  MoveStick( PLCA5V, -1.51,     0,     0,   0,  90, 0);
  MoveStick(PLCA10V, -1.51,     0,     0,   0,  90, 0);
  MoveStick( PLCA8V, -1.51,     0,     0,   0,  90, 0);
  MoveStick( PLCA2V, -1.51,     0,     0,   0,  90, 0);
  // Orange
  MoveStick( PLCC6O,     0,     0,  1.51, 180,   0, 0);
  MoveStick( PLCC5O,     0,     0,  1.51, 180,   0, 0);
  MoveStick( PLCC7O,     0,     0,  1.51, 180,   0, 0);
  MoveStick( PLCC8O,     0,     0,  1.51, 180,   0, 0);
  MoveStick( PLCA9O,     0,     0,  1.51, 180,   0, 0);
  MoveStick(PLCA11O,     0,     0,  1.51, 180,   0, 0);
  MoveStick(PLCA10O,     0,     0,  1.51, 180,   0, 0);
  MoveStick(PLCA12O,     0,     0,  1.51, 180,   0, 0);
  // Face Blanche
  MoveStick( PLCC5W,     0, -1.51,     0, -90,   0, 0);
  MoveStick( PLCC6W,     0, -1.51,     0, -90,   0, 0);
  MoveStick( PLCC1W,     0, -1.51,     0, -90,   0, 0);
  MoveStick( PLCC2W,     0, -1.51,     0, -90,   0, 0);
  MoveStick( PLCA9W,     0, -1.51,     0, -90,   0, 0);
  MoveStick( PLCA5W,     0, -1.51,     0, -90,   0, 0);
  MoveStick( PLCA6W,     0, -1.51,     0, -90,   0, 0);
  MoveStick( PLCA1W,     0, -1.51,     0, -90,   0, 0);
  // Face Jaune
  MoveStick( PLCC4J,     0,  1.51,     0,  90,   0, 0);
  MoveStick( PLCC3J,     0,  1.51,     0,  90,   0, 0);
  MoveStick( PLCC8J,     0,  1.51,     0,  90,   0, 0);
  MoveStick( PLCC7J,     0,  1.51,     0,  90,   0, 0);
  MoveStick( PLCA4J,     0,  1.51,     0,  90,   0, 0);
  MoveStick( PLCA8J,     0,  1.51,     0,  90,   0, 0);
  MoveStick( PLCA7J,     0,  1.51,     0,  90,   0, 0);
  MoveStick(PLCA12J,     0,  1.51,     0,  90,   0, 0);
end;

procedure TFormMain.SortCubes();
begin
  // les cubes coins
  SortCube( CC1, -3, -3, -3);
  SortCube( CC2,  3, -3, -3);
  SortCube( CC3,  3,  3, -3);
  SortCube( CC4, -3,  3, -3);
  SortCube( CC5, -3, -3,  3);
  SortCube( CC6,  3, -3,  3);
  SortCube( CC7,  3,  3,  3);
  SortCube( CC8, -3,  3,  3);
  // les cubes arretes
  SortCube( CA1,  0, -3, -3);
  SortCube( CA2, -3,  0, -3);
  SortCube( CA3,  3,  0, -3);
  SortCube( CA4,  0,  3, -3);
  SortCube( CA5, -3, -3,  0);
  SortCube( CA6,  3, -3,  0);
  SortCube( CA7,  3,  3,  0);
  SortCube( CA8, -3,  3,  0);
  SortCube( CA9,  0, -3,  3);
  SortCube(CA10, -3,  0,  3);
  SortCube(CA11,  3,  0,  3);
  SortCube(CA12,  0,  3,  3);
end;

procedure TFormMain.SortCube(TheCube: TCube; Px, Py, Pz: Single);
begin
  TheCube.ResetRotationAngle;
  TheCube.Position.X := Px;
  TheCube.Position.Y := Py;
  TheCube.Position.Z := Pz;
end;

{$ENDREGION}
{$REGION 'Jouer des mouvements enregistrés'}

procedure TFormMain.cbFunChange(Sender: TObject);
begin
  if cbFun.ItemIndex <> -1 then
  begin
    cbFun.DropDown; // Pour refermer immédiatement la dropdown
    SortRubik();
    PlayMvt(FUN_VALUES[cbFun.ItemIndex]);
  end;
end;

procedure TFormMain.PlayMvt(TheMvts: AnsiString);
var
  I: Integer;
  iMax: Integer;
begin
  // Platform.SetCursor(nil, crHourGlass); // ProcessMessages casse le curseur
  MvtAuto            := True;  // On enchaine une série (l'utilisateur ne peut pas faire de modif manuelle)
  iMax               := Length(TheMvts);
  pbFun.Value        := 1;
  pbFun.Max          := iMax;
  pbFun.Visible      := True;
  recCommand.Enabled := False; // desactiver le panneau de commandes
  for I := 1 to iMax do
  begin
    Rotate(TheMvts[I]);
    pbFun.Value := pbFun.Value + 1;
  end;
  MvtAuto            := False; // La série est terminée, l'utilisateur peut à nouveau jouer
  pbFun.Visible      := False;
  recCommand.Enabled := True;  // le panneau de commande redevient actif
end;

{$ENDREGION}
{$REGION 'Ouverture de la page Web'}

procedure TFormMain.LbInfoLinkClick(Sender: TObject);
begin
  TMisc.Open((Sender as TLabel).Text);
end;
{$ENDREGION}
{$REGION 'Rotation automatique du cube'}

/// *************************************************
/// Déplacement de la caméra autour du rubik's cube
/// *************************************************
Procedure TFormMain.ArcDialXChange(Sender: TObject);
begin
  Dummy.RotationAngle.X := ArcDialX.Value;
  Dummy.RotationAngle.Y := ArcDialY.Value;
  Dummy.RotationAngle.Z := ArcDialZ.Value;
end;

Procedure TFormMain.CbRotateXChange(Sender: TObject);
begin
  FaAutoRotateX.Stop;
  if CbRotateX.IsChecked then
    FaAutoRotateX.Start;
end;

Procedure TFormMain.CbRotateYChange(Sender: TObject);
begin
  FaAutoRotateY.Stop;
  if CbRotateY.IsChecked then
    FaAutoRotateY.Start;
end;

Procedure TFormMain.CbRotateZChange(Sender: TObject);
begin
  FaAutoRotateZ.Stop;
  if CbRotateZ.IsChecked then
    FaAutoRotateZ.Start;
end;
{$ENDREGION}
{$REGION 'Les lumières'}

procedure TFormMain.RbAmbianceChange(Sender: TObject);
Var
  I: Integer;
  Color: TAlphaColor;
begin
  MainLight.Enabled := RbSpotLight.IsChecked;
  BwTbLight.Enabled := RbSpotLight.IsChecked;

  if RbSpotLight.IsChecked then
  begin
    Color := claNull;
    SetLight;
  end
  else
    Color := claWhite;
  for I := 0 to FormMain.ComponentCount - 1 do
  begin
    // les stickers
    if (FormMain.Components[I] Is TPlane) then
      (FormMain.Components[I] As TPlane).material.Emissive := Color;
    // Les cubes pivots
    if (FormMain.Components[I] Is TCube) then
      if (FormMain.Components[I] As TCube).Tag = 1 then
        (FormMain.Components[I] As TCube).material.Emissive := Color;
  end;
end;

procedure TFormMain.SetLight;
var
  cLight: TAlphaColorRec;
begin
  cLight.R := Round(255 * BwTbLight.Value);
  cLight.G := Round(255 * BwTbLight.Value);
  cLight.B := Round(255 * BwTbLight.Value);
  MainLight.Diffuse := cLight.Color;
  Viewport3D2.Repaint;
end;

procedure TFormMain.BwTbLightChange(Sender: TObject);
begin
  SetLight;
end;

{$ENDREGION}

end.

Conclusion :


A/ Caméra et Dummy

Premier point important. Il existe 2 façons de voir un objet sous tous ses angles.
Soit faire pivoter l'objet (rotationAngle)
Soit pivoter autour de l'objet en continuant à le fixer (camera et Dummy)

Le premier cas convient tres bien avec un seul objet. Seuls les rotationAngle XYZ varient.
Dans tous les autres cas utilisez la seconde méthode. Ainsi les coordonnées X,Y et Z des objets resteront
bien mieux maitrisables.

B/ Notions diverses

Le rubik est composé :
-D'un cube central (TCube) celui qui est invisible.
-De 6 cubes Pivots (TCube) sitée au centre,avec un rendu Bitmap pour la couleur
-6 Composants anitmations (TFloatAnimation) , un par pivot. Ils sont enfant des Pivots et mémorisent l'axe de rotation du pivot.
-8 Cubes coins (TCube) Noir avec 3 facettes(TPlane) chacuns. Nommé Stickers dans le source et les commentaires.
-12 cubes Arretes (TCube) Noir avec 2 facettes(TPlane) chacuns. Nommé Stickers dans le source et les commentaires.

C.1 Une fiche dispose de composants accèssibles depuis toujours avec ComponentCount et Components[]. Les objets 3D
eux gerent des enfants accessibles avec ChildrenCount et Children[]. Pensez y !

C.2 Rotation des enfants
La rotation d'un parent declenche la rotation de ses enfants autour du même axe (principe utilisé dans le prog).
A l'issue de la rotation, les enfants ont gardé les mêmes positions X,Y,Z et rotationAngle puisque directement
lié a leur parent. Bien retenir que les positions X,Y,Z et rotationAngle sont donc relatif au parent.
Seules les positions Absolues Varient.

C.3 Changement de parent
Pour effectuer une rotation le programme recherche les cubes coins et cubes arrêtes situés sur le même plan que
le cube central devant pivoter et en fait ses enfants, Puis s'effectue la rotation. Enfin, les cubes enfants
sont rendu au cube central.

Problème, le changement de parent replace l'enfant avec le même décalage et la même rotation qu'il avait avec son ex parent(4.2).
les rotationsAngles et le repositionnement relatif au cube centrale doivent être refait (c'est a vous de le gerer)

C.4 Animation en Thread
Les animations sont threadées, il faut donc attendre la fin d'une rotation pour en lancer une autre

C.6 Pour finir

C.6 Pour finir

les cubes coins sont sensibles au clic souris pour les rotations de face. Un tirer-lâcher sur la zone
grise permet une rotation de tout le cube.

La partie 'Fun' exécute des figures pré-enregistrées.

Modifiez la taille de la fenêtre, changez l'éclairage, changez les animations, vitesse, jouez les auto-rotations
et vous verrez la puissance de l'outil Firemonkey

Merci à Whiler pour son Aide.
Pour ceux qui veulent tester l'exe
ftp://ftp2.p2f-logiciels.com/pflogicie/SetupRubik.exe

Pascal Fonteneau Alias Fireman

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.