[C#] VirtualRubik

    • Release
    • Open Source

    Es gibt 12 Antworten in diesem Thema. Der letzte Beitrag () ist von StarGate01.

      [C#] VirtualRubik

      Name des Programms:
      VirtualRubik

      Beschreibung:
      Ursprünglich entstanden aus [C#] 3D Rubiks Cube zeichnen diesem Thread (den ich empfehle zu lesen) entwickelte (s)ich ein Renderer für einen Rubik-Würfel. Hier findet man nun meine Implementierung mit einer GUI dazu.

      Verwendung
      Bitte diesen Absatz sorgsam lesen.
      Der Würfel kann mit der Rechten oder Mittleren Maustaste gedrückt rotiert werden.
      Die Knöpfe und das Hauptmenü sollten relativ selbsterklärend sein, der Knopf neben dem Schichtenselektor gibt die Drehrichtung an.
      Zum Bewegen einer Schicht mit der Maus wählt man zunächst eine Fläche aus (Linksklick), und dann eine zweite (Linksklick).
      Dabei müssen die beiden Markierung auf einer Würfelseite sein.
      Die durch die beiden Markierungen eindeutig bestimmte Schicht wird nun automatisch gedreht, und zwar so als würde die zweite Markierung die Schicht "drücken".
      Bei Fehlern hilft euch auch die untere Statusleiste.
      ESC oder ENTF heben die aktuelle Auswahl auf.
      Die obere Statusleiste zeigt die momentan gehoverte Fläche an.

      Screenshot(s):


      Changelog
      Spoiler anzeigen

      Quellcode

      1. Version 1.0
      2. - Erster Release
      3. Version 1.1
      4. - Dreh-Bug verkleinert
      5. - Komfortable Maussteuerung eingeführt
      6. - Statusleisten eingeführt
      7. Version 1.2
      8. - Maussteuerung verbessert
      9. - Flächenwahl erleichtert
      10. - Kleinere Bugfixes
      11. Version 1.3
      12. - Fenster in der Größe änderbar
      13. - Hauptmenü hinzugefügt
      14. - Steuerung kann versteckt werden
      15. - ESC oder ENTF zum Aufheben von Markierung
      16. - Bugfixes

      Verwendete Programmiersprache und IDE:
      Visual C# (IDE: Visual Studio 2010 Professional)

      Systemanforderungen:
      .NET Framework 4.0 (JA!)

      Download:
      Siehe Anhang.

      Lizenz/Weitergabe:
      Freeware und Opensource. Ich könnt den Code gerne selbst verwenden, aber bitte seid so fair und erwähnt mich irgendwo :D



      Forks
      RubikCubeSolver von @Switcherlapp97:
      [C#] RubikCubeSolver

      Dateien
      SᴛᴀʀGᴀᴛᴇ01

      Dieser Beitrag wurde bereits 12 mal editiert, zuletzt von „StarGate01“ ()

      Das Verhalten beim Drehen der Ansicht ist äußerst komisch. Je nach dem wie ich den Würfel drehe verändert es sich. Sollte aber so sein, dass wenn ich nach unten ziehe, dass immer das Selbe passiert.


      Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.
      Sieht echt toll aus :thumbsup:
      Ich habe ja generell sehr viel Respekt vor 3D-Sachen und es begeistert mich immer wieder, dass sowas mit Algorithmen etc. möglich ist. Muss zugeben, das ist echt was cooles. Wenn du jetzt die angesprochenen Bugs noch behebst (es war ja nur einer :P), dann ist es wirklich nice. Mein Lob.
      #define for for(int z=0;z<2;++z)for // Have fun!
      Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

      Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Trade“ ()

      Nett gemacht. Meinst du du kriegst das noch hin dass man die layer per swipe mit rechsklick direkt in der 3D sicht rotieren kann? v.a. weil die layer in der combobox fest sind und sich nicht der rotation anpassen wäre es sonst recht unspielbar
      Ist das gewollt, dass wenn man z.b. von rechts nach links swiped es auch von rechts nach links gedreht wird aber von oben nach unten, genau anders herum ? Wenn ja, dann finde ich es irgentwie komisch. Aber sonst echt Respekt, mir gefallen 3D Animationen die man allein in VB/C# macht :) Vielleicht wäre ein Algorithmus cool, der den Würfel auflöst (speedcube.de/)
      Version 1.1 ist da.
      Ich habe den mehrfach bemängelten Dreh-Bug behoben auf ein erträgliches Maß reduziert.
      Das wirklich wichtige am Update ist aber die Maussteuerung, die es erlaubt mit der Maus Schichten zu drehen.
      Zur Verwendung bitte den Startpost lesen, dort ist auch der Download.
      SᴛᴀʀGᴀᴛᴇ01
      @StarGate01
      Irgendwie stimmt das mit den Richtungen beim Rotieren der Flächen noch nicht ganz. Die Drehbewegungen der Top-Fläche, Left-Fläche und Front-Fläche sind verkehrt. Bei diesen drei Flächen müsstest du bei Clockwise eine Drehung der Fläche gegen den Uhrzeigersinn durchführen und umgekehrt.

      Switcherlapp97
      RubiksCubeSolver


      Jetzt im Showroom

      Version 1.2

      Neu in Version 1.2 ist die erleichterte Flächenwahl durch Ausgrauen nicht erlaubter Flächen und das Hervorheben Erlaubter.
      Außerdem wurden kleine Bugs gefixed, so ist nun die Scramble-Funktion viel schneller.
      Der Dreh-Bug ist leider noch drin, ich forsche aber schon daran.
      SᴛᴀʀGᴀᴛᴇ01
      @StarGate01
      Mal wieder ein cooles Update :thumbsup:

      Hier noch ein paar Sachen, die mir aufgefallen sind:
      - Wenn man bei der ersten Auswahl, nicht auf den Würfel klickt, wird das als erste Auswahl angenommen und alle Felder des Cubes werden Schwarz. Ist das so gewollt?
      - Vielleicht noch eine kleine Verbesserung für die Maussteurung: Bei einem Doppelklick auf die zweite Auswahlfläche, könnte man vielleicht eine 180° Drehung der Seite durchführen.
      - So aus Gewohnheit drücke ich immer, wenn ich eine falsche erste Auswahl gemacht habe auf Esc, um die Auswahl zurückzusetzen. Es wäre cool, wenn man mit der Esc-Taste, die Auswahl wirklich zurücksetzen könnte :D

      Wie ich in meinem vorherigen Post beschrieben habe, stimmen die Drehrichtungen noch nicht ganz überein. Ich habe mal ein bisschen herumprobiert und bin zu einem Ergebnis gekommen, das zwar funktioniert, allerdings denke ich, dass mein Code auf jeden Fall noch vereinfacht und verbessert werden kann. Ich poste meine Lösung trotzdem mal hier. Vielleicht hilft sie dir beim Beheben des Drehrichtung-Bugs ;)

      Hier mein Ergebnis

      In der RubikManger.cs die Rotating-Methode erweitern:

      C-Quellcode

      1. public void Rotate90(Cube3D.RubikPosition layer, bool direction, int steps)
      2. {
      3. if (!Rotating)
      4. {
      5. Rotating = true;
      6. rotationLayer = layer;
      7. rotationStep = (double)90 / (double)steps;
      8. rotationTarget = 90;
      9. if (direction && (layer == Cube3D.RubikPosition.BottomLayer || layer == Cube3D.RubikPosition.BackSlice || layer == Cube3D.RubikPosition.RightSlice || layer == Cube3D.RubikPosition.MiddleSlice_Sides) ||
      10. !direction && (layer == Cube3D.RubikPosition.TopLayer || layer == Cube3D.RubikPosition.FrontSlice || layer == Cube3D.RubikPosition.LeftSlice ||
      11. layer == Cube3D.RubikPosition.MiddleLayer || layer == Cube3D.RubikPosition.MiddleSlice))
      12. {
      13. rotationStep *= (-1);
      14. rotationTarget = -90;
      15. }
      16. }
      17. }

      Und nun in der Form1.cs den Code im MouseClick-Event der Form erweitern (Hier denke ich, dass der Code noch gekürzt werden kann - sind doch recht viele If-Abfragen geworden :D :(

      C-Quellcode

      1. private void Form1_MouseClick(object sender, MouseEventArgs e)
      2. {
      3. if (e.Button == System.Windows.Forms.MouseButtons.Left)
      4. {
      5. if (oldSelection.cubePos == Cube3D.RubikPosition.None || oldSelection.facePos == Face3D.FacePosition.None)
      6. {
      7. if (!Cube3D.isCorner(currentSelection.cubePos))
      8. {
      9. oldSelection = currentSelection;
      10. rubikManager.RubikCube.cubes.ForEach(c => c.Faces.ToList().ForEach(f =>
      11. {
      12. if (currentSelection.cubePos != c.Position && !Cube3D.isCenter(c.Position) && currentSelection.facePos == f.Position)
      13. {
      14. Cube3D.RubikPosition assocLayer = Face3D.layerAssocToFace(currentSelection.facePos);
      15. Cube3D.RubikPosition commonLayer = Cube3D.getCommonLayer(currentSelection.cubePos, c.Position, assocLayer);
      16. if (commonLayer != Cube3D.RubikPosition.None && c.Position.HasFlag(commonLayer))
      17. {
      18. f.Selection |= Face3D.SelectionMode.Possible;
      19. }
      20. else
      21. {
      22. f.Selection |= Face3D.SelectionMode.NotPossible;
      23. }
      24. }
      25. else
      26. {
      27. f.Selection |= Face3D.SelectionMode.NotPossible;
      28. }
      29. }));
      30. toolStripStatusLabel1.Text = "First selection: [" + currentSelection.cubePos.ToString() + "] | " + currentSelection.facePos.ToString(); ;
      31. }
      32. else
      33. {
      34. rubikManager.RubikCube.cubes.ForEach(c => c.Faces.ToList().ForEach(f => f.Selection = Face3D.SelectionMode.None));
      35. toolStripStatusLabel1.Text = "Error: Invalid first selection, must not be a corner";
      36. }
      37. }
      38. else
      39. {
      40. if (currentSelection.cubePos == Cube3D.RubikPosition.None || currentSelection.facePos == Face3D.FacePosition.None)
      41. {
      42. toolStripStatusLabel1.Text = "Ready";
      43. }
      44. else
      45. {
      46. if (currentSelection.cubePos != oldSelection.cubePos)
      47. {
      48. if (!Cube3D.isCenter(currentSelection.cubePos))
      49. {
      50. if (oldSelection.facePos == currentSelection.facePos)
      51. {
      52. Cube3D.RubikPosition assocLayer = Face3D.layerAssocToFace(oldSelection.facePos);
      53. Cube3D.RubikPosition commonLayer = Cube3D.getCommonLayer(oldSelection.cubePos, currentSelection.cubePos, assocLayer);
      54. Boolean direction = true;
      55. if (commonLayer == Cube3D.RubikPosition.TopLayer || commonLayer == Cube3D.RubikPosition.MiddleLayer)
      56. {
      57. if (((oldSelection.facePos == Face3D.FacePosition.Front) && currentSelection.cubePos.HasFlag(Cube3D.RubikPosition.RightSlice))
      58. || ((oldSelection.facePos == Face3D.FacePosition.Right) && currentSelection.cubePos.HasFlag(Cube3D.RubikPosition.BackSlice))
      59. || ((oldSelection.facePos == Face3D.FacePosition.Back) && currentSelection.cubePos.HasFlag(Cube3D.RubikPosition.LeftSlice))
      60. || ((oldSelection.facePos == Face3D.FacePosition.Left) && currentSelection.cubePos.HasFlag(Cube3D.RubikPosition.FrontSlice))) direction = false;
      61. }
      62. if (commonLayer == Cube3D.RubikPosition.BottomLayer)
      63. {
      64. if (((oldSelection.facePos == Face3D.FacePosition.Front) && currentSelection.cubePos.HasFlag(Cube3D.RubikPosition.LeftSlice))
      65. || ((oldSelection.facePos == Face3D.FacePosition.Right) && currentSelection.cubePos.HasFlag(Cube3D.RubikPosition.FrontSlice))
      66. || ((oldSelection.facePos == Face3D.FacePosition.Back) && currentSelection.cubePos.HasFlag(Cube3D.RubikPosition.LeftSlice))
      67. || ((oldSelection.facePos == Face3D.FacePosition.Left) && currentSelection.cubePos.HasFlag(Cube3D.RubikPosition.BackSlice))) direction = false;
      68. }
      69. if (commonLayer == Cube3D.RubikPosition.MiddleSlice_Sides || commonLayer == Cube3D.RubikPosition.RightSlice)
      70. {
      71. if (((oldSelection.facePos == Face3D.FacePosition.Top) && currentSelection.cubePos.HasFlag(Cube3D.RubikPosition.FrontSlice))
      72. || ((oldSelection.facePos == Face3D.FacePosition.Front) && currentSelection.cubePos.HasFlag(Cube3D.RubikPosition.BottomLayer))
      73. || ((oldSelection.facePos == Face3D.FacePosition.Bottom) && currentSelection.cubePos.HasFlag(Cube3D.RubikPosition.BackSlice))
      74. || ((oldSelection.facePos == Face3D.FacePosition.Back) && currentSelection.cubePos.HasFlag(Cube3D.RubikPosition.TopLayer))) direction = false;
      75. }
      76. if (commonLayer == Cube3D.RubikPosition.LeftSlice)
      77. {
      78. if (((oldSelection.facePos == Face3D.FacePosition.Top) && currentSelection.cubePos.HasFlag(Cube3D.RubikPosition.BackSlice))
      79. || ((oldSelection.facePos == Face3D.FacePosition.Front) && currentSelection.cubePos.HasFlag(Cube3D.RubikPosition.TopLayer))
      80. || ((oldSelection.facePos == Face3D.FacePosition.Bottom) && currentSelection.cubePos.HasFlag(Cube3D.RubikPosition.FrontSlice))
      81. || ((oldSelection.facePos == Face3D.FacePosition.Back) && currentSelection.cubePos.HasFlag(Cube3D.RubikPosition.BottomLayer))) direction = false;
      82. }
      83. if (commonLayer == Cube3D.RubikPosition.BackSlice)
      84. {
      85. if (((oldSelection.facePos == Face3D.FacePosition.Top) && currentSelection.cubePos.HasFlag(Cube3D.RubikPosition.RightSlice))
      86. || ((oldSelection.facePos == Face3D.FacePosition.Right) && currentSelection.cubePos.HasFlag(Cube3D.RubikPosition.BottomLayer))
      87. || ((oldSelection.facePos == Face3D.FacePosition.Bottom) && currentSelection.cubePos.HasFlag(Cube3D.RubikPosition.LeftSlice))
      88. || ((oldSelection.facePos == Face3D.FacePosition.Left) && currentSelection.cubePos.HasFlag(Cube3D.RubikPosition.TopLayer))) direction = false;
      89. }
      90. if (commonLayer == Cube3D.RubikPosition.MiddleSlice || commonLayer == Cube3D.RubikPosition.FrontSlice)
      91. {
      92. if (((oldSelection.facePos == Face3D.FacePosition.Top) && currentSelection.cubePos.HasFlag(Cube3D.RubikPosition.LeftSlice))
      93. || ((oldSelection.facePos == Face3D.FacePosition.Right) && currentSelection.cubePos.HasFlag(Cube3D.RubikPosition.TopLayer))
      94. || ((oldSelection.facePos == Face3D.FacePosition.Bottom) && currentSelection.cubePos.HasFlag(Cube3D.RubikPosition.RightSlice))
      95. || ((oldSelection.facePos == Face3D.FacePosition.Left) && currentSelection.cubePos.HasFlag(Cube3D.RubikPosition.BottomLayer))) direction = false;
      96. }
      97. if (commonLayer != Cube3D.RubikPosition.None)
      98. {
      99. if (groupBox1.Enabled)
      100. {
      101. groupBox1.Enabled = false;
      102. rotationTicks = 25;
      103. rubikManager.Rotate90(commonLayer, direction, rotationTicks);
      104. comboBox1.Text = commonLayer.ToString();
      105. toolStripStatusLabel1.Text = "Rotating " + commonLayer.ToString() + " " + ((direction) ? "Clockwise" : "Counter-Clockwise");
      106. }
      107. }
      108. else
      109. {
      110. toolStripStatusLabel1.Text = "Error: Invalid second selection, does not specify distinct layer";
      111. }
      112. }
      113. else
      114. {
      115. toolStripStatusLabel1.Text = "Error: Invalid second selection, must match orientation of first selection";
      116. }
      117. }
      118. else
      119. {
      120. toolStripStatusLabel1.Text = "Error: Invalid second selection, must not be a center";
      121. }
      122. }
      123. else
      124. {
      125. toolStripStatusLabel1.Text = "Error: Invalid second selection, must not be first selection";
      126. }
      127. }
      128. rubikManager.RubikCube.cubes.ForEach(c => c.Faces.ToList().ForEach(f => f.Selection = Face3D.SelectionMode.None));
      129. oldSelection = new RubikManager.PositionSpec() { cubePos = Cube3D.RubikPosition.None, facePos = Face3D.FacePosition.None };
      130. currentSelection = new RubikManager.PositionSpec() { cubePos = Cube3D.RubikPosition.None, facePos = Face3D.FacePosition.None };
      131. }
      132. }
      133. }

      Kurzer Kommentar: Die Seiten Top, Bottom, Right, Left, Front und Back werden nun korrekt im Uhrzeigersinn bzw. gegen den Uhrzeigersinn gedreht. Bei den mittleren Ebenen bin ich mir persönlich nicht ganz sicher, wie die korrekten Drehrichtungen lauten sollen.

      Freue mich auf weitere Updates
      Switcherlapp97
      RubiksCubeSolver


      Jetzt im Showroom