lj-clitools/generators/turtle.py

658 lines
19 KiB
Python
Raw Normal View History

2020-11-11 16:31:08 +00:00
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
Turtle library laser emulation
v0.1b
by Sam Neurohack
2020-11-11 21:14:38 +00:00
from proton photon
2020-11-11 16:31:08 +00:00
"""
from __future__ import print_function
import time
import argparse
import sys
import math
from HersheyFonts import HersheyFonts
name="generator::turtle"
def debug(*args, **kwargs):
if( verbose == False ):
return
print(*args, file=sys.stderr, **kwargs)
argsparser = argparse.ArgumentParser(description="Turtle graphics generator")
argsparser.add_argument("-f","--fps",help="Frame Per Second",default=30,type=int)
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose output")
argsparser.add_argument("-x","--LasCenterX",help="geometrical center X position",default=350,type=int)
argsparser.add_argument("-y","--LasCenterY",help="geometrical center Y position",default=350,type=int)
argsparser.add_argument("-p","--police",help="Herschey font to use",default="futural",type=str)
argsparser.add_argument("-m","--mode",help="Mode to use : ",default="clitools",type=str)
args = argsparser.parse_args()
fps=args.fps
verbose=args.verbose
mode = args.mode
LasCenterX = args.LasCenterX
LasCenterY = args.LasCenterY
fontname = args.police
Allfonts = ['futural', 'astrology', 'cursive', 'cyrilc_1', 'cyrillic', 'futuram', 'gothgbt', 'gothgrt', 'gothiceng', 'gothicger', 'gothicita', 'gothitt', 'greek', 'greekc', 'greeks', 'japanese', 'markers', 'mathlow', 'mathupp', 'meteorology', 'music', 'rowmand', 'rowmans', 'rowmant', 'scriptc', 'scripts', 'symbolic', 'timesg', 'timesi', 'timesib', 'timesr', 'timesrb']
thefont = HersheyFonts()
#thefont.load_default_font()
thefont.load_default_font(fontname)
thefont.normalize_rendering(120)
CurrentColor = 255
CurrentAngle = 0.0
CurrentX = 0.0
CurrentY = 0.0
shape = []
optimal_looptime = 1 / fps
debug(name+" optimal looptime "+str(optimal_looptime))
#
# Color functions
#
# input hexcode = '0xff00ff'
def hex2rgb(hexcode):
hexcode = hexcode[2:]
return tuple(int(hexcode[i:i+2], 16) for i in (0, 2, 4))
#return tuple(map(ord,hexcode[1:].decode('hex')))
# input rgb=(255,0,255) output '0xff00ff'
def rgb2hex(rgb):
return '0x%02x%02x%02x' % tuple(rgb)
#def rgb2hex(r, g, b):
# return hex((r << 16) + (g << 8) + b)
def rgb2int(rgb):
return int('0x%02x%02x%02x' % tuple(rgb),0)
#def rgb2int(r,g,b):
# return int('0x%02x%02x%02x' % (r,g,b),0)
def int2rgb(intcode):
#hexcode = '0x{0:06X}'.format(intcode)
hexcode = '{0:06X}'.format(intcode)
return tuple(int(hexcode[i:i+2], 16) for i in (0, 2, 4))
#
# Turtle
#
def fd(distance):
forward(distance)
def forward(distance):
global CurrentX, CurrentY, shape
#Move the turtle forward by the specified distance, in the direction the turtle is headed.
rad = CurrentAngle * math.pi / 180
CurrentX = distance * math.cos(rad) + CurrentX
CurrentY = distance * math.sin(rad) + CurrentY
shape.append([CurrentX + LasCenterX , CurrentY + LasCenterY , CurrentColor])
def back(distance):
backward(distance)
def bk(distance):
backward(distance)
def backward(distance):
global CurrentX, CurrentY, shape
#Move the turtle backward by distance, opposite to the direction the turtle is headed. Do not change the turtles heading.
rad = (CurrentAngle+180) * math.pi / 180
CurrentX = distance * math.cos(rad) + CurrentX
CurrentY = distance * math.sin(rad) + CurrentY
shape.append([CurrentX + LasCenterX, CurrentY + LasCenterY, CurrentColor])
def right(angle):
rt(angle)
def rt(angle):
global CurrentAngle
#Turn turtle right by angle units. (Units are by default degrees, but can be set via the degrees() and radians() functions.) Angle orientation depends on the turtle mode, see mode().
CurrentAngle = CurrentAngle + angle
def left(angle):
lt(angle)
def lt(angle):
global CurrentAngle
#Turn turtle left by angle units. (Units are by default degrees, but can be set via the degrees() and radians() functions.) Angle orientation depends on the turtle mode, see mode().
CurrentAngle = CurrentAngle - angle
def goto(x, y=None):
setposition(x, y=None)
def setpos(x, y=None):
setposition(x, y=None)
def setposition(x, y=None):
global CurrentX, CurrentY, shape
#If y is None, x must be a pair of coordinates or a Vec2D (e.g. as returned by pos()).
# Move turtle to an absolute position. If the pen is down, draw line. Do not change the turtles orientation.
CurrentX = x
CurrentY = y
shape.append([CurrentX + LasCenterX, CurrentY + LasCenterY, CurrentColor])
def setx(x):
global CurrentX
#Set the turtles first coordinate to x, leave second coordinate unchanged.
CurrentX = x
def sety(y):
global CurrentY
#Set the turtles second coordinate to y, leave first coordinate unchanged.
CurrentY = y
def setheading(to_angle):
global CurrentAngle
#Parameters: to_angle a number (integer or float)
CurrentAngle = to_angle
def home():
global CurrentX, CurrentY, CurrentAngle , shape
#Move turtle to the origin coordinates (0,0) and set its heading to its start-orientation (which depends on the mode, see mode()).
CurrentX = 0.0
CurrentY = 0.0
CurrentAngle = 0.0
shape.append([CurrentX + LasCenterX, CurrentY + LasCenterY, CurrentColor])
def circle(radius, extent=None, steps=None):
#Draw a circle with given radius. The center is radius units left of the turtle; extent an angle determines which part of the circle is drawn. If extent is not given, draw the entire circle. If extent is not a full circle, one endpoint of the arc is the current pen position. Draw the arc in counterclockwise direction if radius is positive, otherwise in clockwise direction. Finally the direction of the turtle is changed by the amount of extent.
print("Not yet in Laser turtle.")
'''
>>> turtle.home()
>>> turtle.position()
(0.00,0.00)
>>> turtle.heading()
0.0
>>> turtle.circle(50)
>>> turtle.position()
(-0.00,0.00)
>>> turtle.heading()
0.0
>>> turtle.circle(120, 180) # draw a semicircle
>>> turtle.position()
(0.00,240.00)
>>> turtle.heading()
180.0
'''
def dot(size=None, *color):
#Draw a circular dot with diameter size, using color. If size is not given, the maximum of pensize+4 and 2*pensize is used.
print("Not yet in Laser turtle.")
'''
>>> turtle.home()
>>> turtle.dot()
>>> turtle.fd(50); turtle.dot(20, "blue"); turtle.fd(50)
>>> turtle.position()
(100.00,-0.00)
>>> turtle.heading()
0.0
'''
def stamp():
#Stamp a copy of the turtle shape onto the canvas at the current turtle position. Return a stamp_id for that stamp, which can be used to delete it by calling clearstamp(stamp_id).
print("Not yet in Laser turtle.")
'''
>>> turtle.color("blue")
>>> turtle.stamp()
11
>>> turtle.fd(50)
'''
def clearstamp(stampid):
#Delete stamp with given stampid.
print("Not yet in Laser turtle.")
'''
>>> turtle.position()
(150.00,-0.00)
>>> turtle.color("blue")
>>> astamp = turtle.stamp()
>>> turtle.fd(50)
>>> turtle.position()
(200.00,-0.00)
>>> turtle.clearstamp(astamp)
>>> turtle.position()
(200.00,-0.00)
'''
def clearstamps(n=None):
#Delete all or first/last n of turtles stamps. If n is None, delete all stamps, if n > 0 delete first n stamps, else if n < 0 delete last n stamps.
print("Not yet in Laser turtle.")
'''
>>> for i in range(8):
... turtle.stamp(); turtle.fd(30)
13
14
15
16
17
18
19
20
>>> turtle.clearstamps(2)
>>> turtle.clearstamps(-2)
>>> turtle.clearstamps()
'''
def undo():
#Undo (repeatedly) the last turtle action(s). Number of available undo actions is determined by the size of the undobuffer.
print("Not yet in Laser turtle.")
'''
>>> for i in range(4):
... turtle.fd(50); turtle.lt(80)
...
>>> for i in range(8):
... turtle.undo()
'''
def speed(speed=None):
#Set the turtles speed to an integer value in the range 0..10. If no argument is given, return current speed.
print("Not yet in Laser turtle.")
'''
If input is a number greater than 10 or smaller than 0.5, speed is set to 0. Speedstrings are mapped to speedvalues as follows:
fastest: 0
fast: 10
normal: 6
slow: 3
slowest: 1
Speeds from 1 to 10 enforce increasingly faster animation of line drawing and turtle turning.
Attention: speed = 0 means that no animation takes place. forward/back makes turtle jump and likewise left/right make the turtle turn instantly.
>>>
>>> turtle.speed()
3
>>> turtle.speed('normal')
>>> turtle.speed()
6
>>> turtle.speed(9)
>>> turtle.speed()
9
'''
def position():
pos()
def pos():
#Return the turtles current location (x,y) (as a Vec2D vector).
return (CurrentX,CurrentY)
def towards(x, y=None):
#Return the angle between the line from turtle position to position specified by (x,y), the vector or the other turtle. This depends on the turtles start orientation which depends on the mode - “standard”/”world” or “logo”).
# Currently only toward an x,y point
xDiff = x - CurrentX
yDiff = y - CurrentY
return degrees(atan2(yDiff, xDiff))
def xcor():
#Return the turtles x coordinate.
return CurrentX
def ycor():
#Return the turtles y coordinate.
return CurrentY
def heading():
#Return the turtles current heading (value depends on the turtle mode, see mode())
return CurrentAngle
def distance(x, y=None):
#Return the distance from the turtle to (x,y), the given vector, or the given other turtle, in turtle step units.
dist = math.sqrt((x - CurrentY)**2 + (y - CurrentY)**2)
return dist
def degrees(fullcircle=360.0):
#Set angle measurement units, i.e. set number of “degrees” for a full circle. Default value is 360 degrees.
print("Not yet in Laser turtle.")
'''
>>> turtle.home()
>>> turtle.left(90)
>>> turtle.heading()
90.0
Change angle measurement unit to grad (also known as gon,
grade, or gradian and equals 1/100-th of the right angle.)
>>> turtle.degrees(400.0)
>>> turtle.heading()
100.0
>>> turtle.degrees(360)
>>> turtle.heading()
90.0
'''
def radians():
#Set the angle measurement units to radians. Equivalent to degrees(2*math.pi).
print("Not yet in Laser turtle.")
'''
>>> turtle.home()
>>> turtle.left(90)
>>> turtle.heading()
90.0
>>> turtle.radians()
>>> turtle.heading()
1.5707963267948966
'''
def pendown():
down()
def pd():
down()
def down():
global CurrentColor
#Pull the pen down drawing when moving.
CurrentColor = Color
def penup():
up()
def pu():
up()
def up():
#Pull the pen up no drawing when moving.
global CurrentColor
CurrentColor = 0
def pensize(width=None):
width(width=None)
def width(width=None):
#Set the line thickness to width or return it. If resizemode is set to “auto” and turtleshape is a polygon, that polygon is drawn with the same line thickness. If no argument is given, the current pensize is returned.
print("Not yet in Laser turtle.")
'''
>>> turtle.pensize()
1
>>> turtle.pensize(10) # from here on lines of width 10 are drawn
'''
def pen(pen=None, **pendict):
#Return or set the pens attributes in a “pen-dictionary” with the following key/value pairs:
print("Not yet in Laser turtle.")
'''
shown: True/False
pendown: True/False
pencolor: color-string or color-tuple
fillcolor: color-string or color-tuple
pensize: positive number
speed: number in range 0..10
resizemode: auto or user or noresize
stretchfactor: (positive number, positive number)
outline: positive number
tilt: number
This dictionary can be used as argument for a subsequent call to pen() to restore the former pen-state. Moreover one or more of these attributes can be provided as keyword-arguments. This can be used to set several pen attributes in one statement.
>>>
>>> turtle.pen(fillcolor="black", pencolor="red", pensize=10)
>>> sorted(turtle.pen().items())
[('fillcolor', 'black'), ('outline', 1), ('pencolor', 'red'),
('pendown', True), ('pensize', 10), ('resizemode', 'noresize'),
('shearfactor', 0.0), ('shown', True), ('speed', 9),
('stretchfactor', (1.0, 1.0)), ('tilt', 0.0)]
>>> penstate=turtle.pen()
>>> turtle.color("yellow", "")
>>> turtle.penup()
>>> sorted(turtle.pen().items())[:3]
[('fillcolor', ''), ('outline', 1), ('pencolor', 'yellow')]
>>> turtle.pen(penstate, fillcolor="green")
>>> sorted(turtle.pen().items())[:3]
[('fillcolor', 'green'), ('outline', 1), ('pencolor', 'red')]
'''
def isdown():
#Return True if pen is down, False if its up.
if CurrentColor != 0:
return True
else:
return False
def pencolor(*args):
global CurrentColor
'''
Return or set the pencolor.
Four input formats are allowed:
pencolor()
Return the current pencolor as color specification string or as a tuple (see example). May be used as input to another color/pencolor/fillcolor call.
pencolor(colorstring)
Set pencolor to colorstring, which is a Tk color specification string, such as "red", "yellow", or "#33cc8c".
pencolor((r, g, b))
Set pencolor to the RGB color represented by the tuple of r, g, and b. Each of r, g, and b must be in the range 0..colormode, where colormode is either 1.0 or 255 (see colormode()).
pencolor(r, g, b)
Set pencolor to the RGB color represented by r, g, and b. Each of r, g, and b must be in the range 0..colormode.
If turtleshape is a polygon, the outline of that polygon is drawn with the newly set pencolor.
'''
#print(args, len(args))
if len(args) == 1:
colors = args[0]
#print(colors)
if colors[0]=="#":
CurrentColor = hex2int(colors)
else:
CurrentColor = rgb2int(colors)
print("CurrentColor:",CurrentColor)
else:
print(int2rgb(CurrentColor))
return int2rgb(CurrentColor)
'''
>>>
>>> colormode()
1.0
>>> turtle.pencolor()
'red'
>>> turtle.pencolor("brown")
>>> turtle.pencolor()
'brown'
>>> tup = (0.2, 0.8, 0.55)
>>> turtle.pencolor(tup)
>>> turtle.pencolor()
(0.2, 0.8, 0.5490196078431373)
>>> colormode(255)
>>> turtle.pencolor()
(51.0, 204.0, 140.0)
>>> turtle.pencolor('#32c18f')
>>> turtle.pencolor()
(50.0, 193.0, 143.0)
'''
def fillcolor(*args):
# Return or set the fillcolor.
print("Not yet in Laser turtle.")
def color(*args):
global CurrentColor
#Return or set pencolor and fillcolor.
if len(*args) ==2:
colors = args
if colors[0][0]=="#":
CurrentColor = hex2int(colors[0])
else:
print(int2rgb(CurrentColor),(0,0,0))
return (int2rgb(CurrentColor),(0,0,0))
#rgb2int(rgb)
'''
Several input formats are allowed. They use 0 to 3 arguments as follows:
color()
Return the current pencolor and the current fillcolor as a pair of color specification strings or tuples as returned by pencolor() and fillcolor().
color(colorstring), color((r,g,b)), color(r,g,b)
Inputs as in pencolor(), set both, fillcolor and pencolor, to the given value.
color(colorstring1, colorstring2), color((r1,g1,b1), (r2,g2,b2))
Equivalent to pencolor(colorstring1) and fillcolor(colorstring2) and analogously if the other input format is used.
If turtleshape is a polygon, outline and interior of that polygon is drawn with the newly set colors.
>>>
>>> turtle.color("red", "green")
>>> turtle.color()
('red', 'green')
>>> color("#285078", "#a0c8f0")
>>> color()
((40.0, 80.0, 120.0), (160.0, 200.0, 240.0))
'''
def filling():
# Return fillstate (True if filling, False else).
return False
def begin_fill():
print("Not yet in Laser turtle.")
def end_fill():
#Fill the shape drawn after the last call to begin_fill().
print("Not yet in Laser turtle.")
def reset():
#Delete the turtles drawings from the screen, re-center the turtle and set variables to the default values.
global shape
clear()
home()
def clear():
#Delete the turtles drawings from the screen. Do not move turtle. State and position of the turtle as well as drawings of other turtles are not affected.
global shape
shape = []
def write(arg, move=False, align="left", font=("Arial", 8, "normal")):
global shape
'''
Parameters:
arg object to be written to the TurtleScreen
move True/False
align one of the strings left, center or right
font a triple (fontname, fontsize, fonttype)
Write text - the string representation of arg - at the current turtle position according to align (left, center or right) and with the given font. If move is true, the pen is moved to the bottom-right corner of the text. By default, move is False.
'''
for (x1, y1), (x2, y2) in thefont.lines_for_text(arg):
shape.append([x1, -y1+400, color])
shape.append([x2 ,-y2+400, color])
'''
>>> turtle.write("Home = ", True, align="center")
>>> turtle.write((0,0), True)
'''
def hideturtle():
ht()
def ht():
#Make the turtle invisible. Its a good idea to do this while youre in the middle of doing some complex drawing, because hiding the turtle speeds up the drawing observably.
print("Not yet in Laser turtle.")
#>>> turtle.hideturtle()
def showturtle():
st()
def st():
#Make the turtle visible.
print("Not yet in Laser turtle.")
def delay(delay=None):
#Set or return the drawing delay in milliseconds. (This is approximately the time interval between two consecutive canvas updates.) The longer the drawing delay, the slower the animation.
print("Not yet in Laser turtle.")
'''
Optional argument:
>>> screen.delay()
10
>>> screen.delay(5)
>>> screen.delay()
5
'''
def mainloop():
done()
def done():
#Starts event loop - calling Tkinters mainloop function. Must be the last statement in a turtle graphics program. Must not be used if a script is run from within IDLE in -n mode (No subprocess) - for interactive use of turtle graphics.
while True:
start = time.time()
print(shape, flush=True);
looptime = time.time() - start
if( looptime < optimal_looptime ):
time.sleep( optimal_looptime - looptime)
debug(name+" micro sleep:"+str( optimal_looptime - looptime))
def window_height():
#Return the height of the turtle window.
return LasCenterX * 2
def window_width():
#Return the width of the turtle window.
return LasCenterY*2