2

I am working on a "3D" cube in Python with Turtle. I am trying to find a way so I can make a cursor lock, or just so when you hold right click you can look at the cube from different angles. Since I do not know how to do this, I have a rotating function.

My current code:

import turtle

VERTEXES = [(-1, -1, -1), ( 1, -1, -1), ( 1,  1, -1), (-1,  1, -1),
            (-1, -1,  1), ( 1, -1,  1), ( 1,  1,  1), (-1,  1,  1)]

TRIANGLES = [
    (0, 1, 2), (2, 3, 0),
    (0, 4, 5), (5, 1, 0),
    (0, 4, 3), (4, 7, 3),
    (5, 4, 7), (7, 6, 5),
    (7, 6, 3), (6, 2, 3),
    (5, 1, 2), (2, 6, 5)
]

FOV = 400

# Create turtle,
pointer = turtle.Turtle()

# Turn off move time, makes drawing instant,
turtle.tracer(0, 0)
pointer.up()

def rotate(x, y, r):
  s, c = sin(r), cos(r)
  return x * c - y * s, x * s + y * c

counter = 0
while True:
    # Clear screen,
    pointer.clear()

    # Draw,
    for triangle in TRIANGLES:
        points = []
        for vertex in triangle:
            # Get the X, Y, Z coords out of the vertex iterator,
            x, y, z = VERTEXES[vertex]

            # Rotate,
            x, z = rotate(x, z, counter)
            y, z = rotate(y, z, counter)
            x, y = rotate(x, y, counter)

            # Perspective formula,
            z += 5
            f = FOV / z
            sx, sy = x * f, y * f

            # Add point,
            points.append((sx, sy))
        
        # Draw triangle,
        pointer.goto(points[0][0], points[0][1])
        pointer.down()

        pointer.goto(points[1][0], points[1][1])
        pointer.goto(points[2][0], points[2][1])
        pointer.goto(points[0][0], points[0][1])
        pointer.up()

    # Update,
    turtle.update()

    counter += 0.01
Almog
  • 452
  • 1
  • 7
  • 13
ALEXcodes
  • 63
  • 3
  • Please repeat [on topic](https://stackoverflow.com/help/on-topic) and [how to ask](https://stackoverflow.com/help/how-to-ask) from the [intro tour](https://stackoverflow.com/tour). "Show me how to solve this coding problem?" is off-topic for Stack Overflow. You have to make an honest attempt at the solution, and then ask a *specific* question about your implementation. Stack Overflow is not intended to replace existing tutorials and documentation. Posting code that doesn't relate to your question is not helpful. – Prune Dec 26 '20 at 23:34

2 Answers2

2

First off I just wanna say that Turtle does not have a lot of support for mouse events and other fun things that other frameworks have.

Because of turtles lack of support for mouse up events when it is triggered via screen events this will allow you to right click and not hold it down and it will continue to get the delta of your mouse movements before setting the mouse back to the original position. Once you done dragging the mouse just right click again and it will release the mouse.

The delta will be low, usually between 1-3. If this is to low then add in a time.sleep(1/60). This will lock the fps to 60 frames and will allow the mouse to move more before being locked back to its original position.

I also want to show off this question cause it seems like it has a way of adding in on mouse up and move events to turtle but ill let you look at it to decide if you want to go that route.

Find the cursor's current position in Python turtle

Good luck.

from ctypes import windll, Structure, c_long, byref
from turtle import Turtle, Screen, update


is_dragging = False
start_mouse_pos = (0, 0)


class POINT(Structure):
    _fields_ = [("x", c_long), ("y", c_long)]
    x: int
    y: int


def getMousePosition():
    pt = POINT()
    windll.user32.GetCursorPos(byref(pt))
    return (pt.x, pt.y)


def setMousePosition(pos: tuple):
    windll.user32.SetCursorPos(int(pos[0]), int(pos[1]))


def onScreenClick(x, y):
    global is_dragging, start_mouse_pos
    is_dragging = not is_dragging
    start_mouse_pos = getMousePosition()


turtle = Turtle()
screen = Screen()
screen.onscreenclick(onScreenClick, 3)

while True:
    if is_dragging:
        pos = getMousePosition()
        delta_pos = (start_mouse_pos[0] - pos[0], start_mouse_pos[1] - pos[1])

        # Do something with how much the mouse has moved then set the mouse position back

        setMousePosition(start_mouse_pos)
    update()
TeddyBearSuicide
  • 1,377
  • 1
  • 6
  • 11
0

You should be able to do this with an ondrag() event handler. Below is a rework of your code to remove the automatic movement and substitute the ability to manually move the cube by clicking on the the red corner and moving the mouse while continuing to hold the button down (dragging):

from math import sin, cos, atan2 as atan
from turtle import Screen, Turtle

VERTEXES = [
    (-1, -1, -1), (1, -1, -1), (1, 1, -1), (-1, 1, -1),
    (-1, -1, 1), (1, -1, 1), (1, 1, 1), (-1, 1, 1)
]

TRIANGLES = [
    (0, 1, 2),
    (2, 3, 0),
    (0, 4, 5),
    (5, 1, 0),
    (0, 4, 3),
    (4, 7, 3),
    (5, 4, 7),
    (7, 6, 5),
    (7, 6, 3),
    (6, 2, 3),
    (5, 1, 2),
    (2, 6, 5)
]

FOV = 400

def rotate(x, y, r):
    s, c = sin(r), cos(r)

    return x * c - y * s, x * s + y * c

orientation = 0  # radians

def draw():
    global orientation

    turtle.clear()

    for triangle in TRIANGLES:
        for flag, vertex in enumerate(triangle):
            # Get the X, Y, Z coords out of the vertex iterator
            x, y, z = VERTEXES[vertex]

            # Rotate
            x, z = rotate(x, z, orientation)
            y, z = rotate(y, z, orientation)
            x, y = rotate(x, y, orientation)

            # Perspective formula
            z += 5
            f = FOV / z
            s = x * f, y * f

            # Draw line
            turtle.setposition(s)
            turtle.pendown()

            if not flag:
                start = s

        turtle.setposition(start)
        turtle.penup()

    screen.update()

def orient(x, y):
    global orientation

    turtle.ondrag(None)  # disable handler inside handler

    orientation = atan(y, x)
    draw()

    turtle.ondrag(orient)  # reenable handler

# Turn off move time, makes drawing instant,
screen = Screen()
screen.tracer(False)

# Create turtle,
turtle = Turtle('circle')
turtle.fillcolor('red')
turtle.shapesize(0.5)
turtle.penup()

draw()

turtle.ondrag(orient)

screen.mainloop()

The mouse position to cube position mapping clearly isn't optimal, but should be enough to convince you it is possible and provide a starting point.

cdlane
  • 40,441
  • 5
  • 32
  • 81