diff --git a/clitools/generators/__pycache__/turtle.cpython-38.pyc b/clitools/generators/__pycache__/turtle.cpython-38.pyc index 5b818f5..8251073 100644 Binary files a/clitools/generators/__pycache__/turtle.cpython-38.pyc and b/clitools/generators/__pycache__/turtle.cpython-38.pyc differ diff --git a/clitools/generators/text.py b/clitools/generators/text.py index c1c5c39..63fbcbd 100644 --- a/clitools/generators/text.py +++ b/clitools/generators/text.py @@ -4,8 +4,9 @@ ''' -A text generators using Hershey fonts -https://pypi.org/project/Hershey-Fonts/ +Experimental Laserized Turtle graphics library + +See turtle1.py for example pip3 install Hershey-Fonts diff --git a/clitools/generators/turtle.py b/clitools/generators/turtle.py new file mode 100644 index 0000000..9e26349 --- /dev/null +++ b/clitools/generators/turtle.py @@ -0,0 +1,657 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +""" +Turtle library laser emulation +v0.1b + +by Sam Neurohack +from /team/laser + +""" + +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 turtle’s 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 turtle’s orientation. + + CurrentX = x + CurrentY = y + shape.append([CurrentX + LasCenterX, CurrentY + LasCenterY, CurrentColor]) + + +def setx(x): + global CurrentX + #Set the turtle’s first coordinate to x, leave second coordinate unchanged. + CurrentX = x + + +def sety(y): + global CurrentY + #Set the turtle’s 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 turtle’s 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 turtle’s 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 turtle’s 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 turtle’s 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 turtle’s x coordinate. + return CurrentX + + +def ycor(): + #Return the turtle’s y coordinate. + return CurrentY + + +def heading(): + #Return the turtle’s 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 pen’s 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 it’s 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 turtle’s drawings from the screen, re-center the turtle and set variables to the default values. + global shape + + clear() + home() + +def clear(): + #Delete the turtle’s 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. It’s a good idea to do this while you’re 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 Tkinter’s 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 + + diff --git a/clitools/generators/turtle1.py b/clitools/generators/turtle1.py new file mode 100644 index 0000000..a5919b6 --- /dev/null +++ b/clitools/generators/turtle1.py @@ -0,0 +1,19 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# -*- mode: Python -*- + +''' + +Example using experimental Laserized Turtle graphics library + +''' + +from turtle import * + +pencolor((255,0,0)) + +for i in range(4): + forward(100) + right(90) + +done() \ No newline at end of file