Replicating this design in Python - python

I've been trying to replicate this design using Python.
I am using the Graphics module, obtained here. I can't use any other Graphic Module at the moment.
This code here allows me to draw 5 circles in a line repeated by a loop.
def fdShape():
win = GraphWin(199,199)
centre = Point(20,100)
for y in range(5):
for x in range(5):
centre = Point(x * 40 + 20, y * 40 + 60)
circle = Circle(centre, 20)
circle.setOutline("red")
circle.draw(win)
One problem that I've found with this code is that it leaves a blank line at the top of the window and place the last circle line at the bottom, beyond the borders of the window. Thats the first problem.
The second is using the code to display semi circles displayed in red. As you can see in the image at the top of this page. I'm unsure of how to replicate this picture using Python.
Thank you!

This looks pretty close:
from graphic import *
def main():
repeat = 5
diameter = 40
radius = diameter // 2
offset = radius // 2
win = GraphWin("Graphic Design", diameter*repeat + offset, diameter*repeat)
win.setBackground('white')
for i in range(repeat):
for j in range(repeat):
draw_symbol(win, i % 2,
Point(i*diameter + offset, j*diameter), radius, 'red')
win.getMouse()
win.close()
def draw_symbol(win, kind, lower_left, radius, colour):
centre = Point(lower_left.x+radius, lower_left.y+radius)
circle = Circle(centre, radius)
circle.setOutline('')
circle.setFill(colour)
circle.draw(win)
if kind == 0:
rectangle = Rectangle(lower_left,
Point(lower_left.x+radius, lower_left.y+radius*2))
else:
rectangle = Rectangle(lower_left,
Point(lower_left.x+radius*2, lower_left.y+radius))
rectangle.setOutline('white')
rectangle.setFill('white')
rectangle.draw(win)
circle = Circle(centre, radius)
circle.setWidth(1)
circle.setOutline(colour)
circle.setFill('')
circle.draw(win)
main()

Two issues I see.
GraphWin should be initialized to 200x200, not 199x199.
This line:
centre = Point(x * 40 + 20, y * 40 + 60)
Should most likely be:
centre = Point(x * 40 + 20, y * 40 + 20)

Related

Cutting the codes in the program and making it neater

I'm having a hard time cutting the code and making it into a loop so that it would make the code of the program, neater.
Although my code works as it suppose to be, I think there is a right way of creating it, adding a for loop rather than writing all of these codes, I know there is an easy way to do this, I just couldn't figure how to do it properly. I know I'm suppose to create a for loop.
squares
from graphics import *
def main():
win = GraphWin("Squares", 500, 500)
rect = Rectangle(Point(0,500), Point(500,0))
rect.setFill("Red")
rect.draw(win)
rect2 = Rectangle(Point(20,480), Point(480,20))
rect2.setFill("white")
rect2.draw(win)
rect3 = Rectangle(Point(40,460), Point(460,40))
rect3.setFill("red")
rect3.draw(win)
rect4 = Rectangle(Point(60,440), Point(440,60))
rect4.setFill("white")
rect4.draw(win)
rect5 = Rectangle(Point(80,420), Point(420,80))
rect5.setFill("red")
rect5.draw(win)
rect6 = Rectangle(Point(100,400), Point(400,100))
rect6.setFill("white")
rect6.draw(win)
rect7 = Rectangle(Point(120,380), Point(380,120))
rect7.setFill("red")
rect7.draw(win)
rect8 = Rectangle(Point(140,360), Point(360,140))
rect8.setFill("white")
rect8.draw(win)
rect9 = Rectangle(Point(160,340), Point(340,160))
rect9.setFill("red")
rect9.draw(win)
rect10 = Rectangle(Point(180,320), Point(320,180))
rect10.setFill("white")
rect10.draw(win)
rect11 = Rectangle(Point(200,300), Point(300,200))
rect11.setFill("red")
rect11.draw(win)
rect12 = Rectangle(Point(220,280), Point(280,220))
rect12.setFill("white")
rect12.draw(win)
The results shows squares into some sort of a patchwork
Try the following:
from graphics import *
def main():
win = GraphWin("Squares", 500, 500)
# create all rects
rects = [Rectangle(Point(0 + 20*i,500 - 20*i), Point(500 - 20*i, 0 + 20*i)) for i in range(12)]
# draw all rects
for idx, rect in enumerate(rects):
rect.fill("red" if idx % 2 == 0 else "white")
rect.draw(win)
If the patchwork is just a background and you don't plan on modifying it you could use this:
from graphics import *
def main():
win = GraphWin("Squares", 500, 500)
i = 1
for x in range(0, 221, 20):
rect = Rectangle(Point(x, 500 - x), Point(500 - x,x))
rect.setFill("red" if i % 2 else "white")
rect.draw(win)
i += 1
An alternate approach that only needs to draw half as many rectangles due to using the rectangle's outline as the other color:
SQUARE, WIDTH = 500, 20
def main():
win = GraphWin("Squares", SQUARE, SQUARE)
save_config = dict(DEFAULT_CONFIG)
DEFAULT_CONFIG.update(dict(outline='red', fill='white', width=WIDTH))
for xy in range(WIDTH//2, SQUARE//2, WIDTH*2):
Rectangle(Point(xy, SQUARE - xy), Point(SQUARE - xy, xy)).draw(win)
DEFAULT_CONFIG.update(save_config)
It's fully parameterized so you can fit it to a different size square or have different width stripes by adjusting the SQUARE and WIDTH parameters. Rather than draw 12 rectangles in alternating colors, with the parameters as currently set, it draws 6 white rectangles with red outlines:

homework help? for making a spirograph

SO over break week our teacher gave us a little project to do requiring a Spirograph, here is the code he helped us write before
from graphics import *
from math import *
def ar(a):
return a*3.141592654/180
def main():
x0 = 100
y0 = 100
startangle = 60
stepangle = 120
radius = 50
win = GraphWin()
p1 = Point(x0 + radius * cos(ar(startangle)), y0 + radius * sin(ar(startangle)))
for i in range(stepangle+startangle,360+stepangle+startangle,stepangle):
p2 = Point(x0 + radius * cos(ar(i)), y0 + radius * sin(ar(i)))
Line(p1,p2).draw(win)
p1 = p2
input("<ENTER> to quit...")
win.close()
main()
he then wants us to develop the program that consecutively draws 12 equilateral triangles (rotating the triangle each time by 30 degrees through a full 360 circle). This can be achieved by “stepping” the STARTANGLE parameter. My question I am stuck on where to go from here, what does he mean by "stepping?" I assume making some sort of loop, is it possible someone can give me a push in the right step?
This is a solution using matplotlib. The general procedure would be the same. But you will have to modify it for using the libraries you're allowed to use.
from math import radians, sin, cos
import matplotlib.pyplot as plt
startAngle = 0
stepAngle = 30
origin = (0,0)
points = []
points.append(origin)
points.append((cos(radians(startAngle)), sin(radians(startAngle))))
for i in range(startAngle + stepAngle, 360 + stepAngle, stepAngle):
x = cos(radians(i))
y = sin(radians(i))
points.append((x,y))
points.append(origin)
points.append((x,y))
x,y = zip(*points) #separate the tupples into x and y coordinates.
plt.plot(x,y) #plots the points, drawing lines between each point
plt.show()
plt.plot draw lines between each point in the list. We're adding inn the origin points so we get triangles instead of just a polygon around the center.

Randomly orientated lines drawn off a random point in python

I'm trying to create python program that has several vertical lines which act as boundaries where randomly generated points or "dots" (as referred to in the code) which draw a straight line at a random degree. If the straight line intersects with one of the vertical "boundaries" I want to make it change colour. I have a picture of what I am trying to achieve which will probably explain my situation a bit clearer. The code I post below has drawn the "vertical boundaries" and has the points randomly generated within the region, however that is where I am stuck.
What I am aiming to achieve:
Example of program
My current Code:
setup(750,750)
screen_size = 750
max_coord = (screen_size - 30) / 2
### change the number of dots you have via that variable
num_dots = 500
bgcolor('yellow')
dot_size=5
reset() # Create an empty window
pi = Turtle()
hideturtle()
def parallel_lines(number):
pi.pensize(2)
pi.pencolor('black')
width = pi.window_width()
height = pi.window_height()
pi.setheading(90)
pi.penup()
pi.setposition(width/-2, height/-2)
for i in range(1, number +2):
pi.pendown()
pi.forward(height)
pi.penup()
pi.setposition(width/-2+i*(width/(number+1)),height/-2)
parallel_lines(7)
## centre turtle back in the middle of the page
goto(0,0)
### list to hold the dots
x_coords = []
y_coords = []
### Draw the dots via randomint
penup()
color("blue")
for dot_num in range(num_dots):
dot_pos_x = randint (-max_coord, max_coord)
dot_pos_y = randint (-max_coord, max_coord)
goto(dot_pos_x, dot_pos_y)
dot(dot_size)
x_coords.append(dot_pos_x)
y_coords.append(dot_pos_y)
done()
Thank you in advance for anyone that can help.
Here's an implementation of the program the OP describes. If there is a line intersection, it uses Python 3 turtle's undo feature to remove the line and redraw it in the alternate color:
from turtle import Turtle, Screen
from random import randint, randrange
SCREEN_SIZE = 750
PLANK_COUNT = 8
PINHEAD_SIZE = 5
FLOOR_COLOR = "yellow"
DEFAULT_COLOR = "blue"
CROSSING_COLOR = "red"
screen = Screen()
screen.setup(SCREEN_SIZE, SCREEN_SIZE)
screen.bgcolor(FLOOR_COLOR)
# configure numbers to replicate Lazzarini's setup
NUMBER_PINS = 3408
PIN_LENGTH = 78.125
PLANK_WIDTH = screen.window_width() / PLANK_COUNT
def parallel_lines(turtle, width, height):
turtle.penup()
turtle.setheading(90)
turtle.sety(height / -2)
x_coordinates = []
for i in range(PLANK_COUNT + 1):
x = i * PLANK_WIDTH - width / 2
turtle.setx(x)
turtle.pendown()
turtle.forward(height)
turtle.penup()
turtle.left(180)
x_coordinates.append(x)
return x_coordinates
pi = Turtle(visible=False)
pi.speed("fastest")
x_coordinates = parallel_lines(pi, screen.window_width(), screen.window_height())
def crosses(x0, x1, coordinates):
for coordinate in coordinates:
if x0 <= coordinate <= x1 or x1 <= coordinate <= x0:
return True
return False
previous_crossings = crossings = 0
max_coord = screen.window_width() / 2
for pin in range(NUMBER_PINS):
x0, y0 = randint(-max_coord, max_coord), randint(-max_coord, max_coord)
pi.color(DEFAULT_COLOR)
pi.goto(x0, y0)
pi.dot(PINHEAD_SIZE)
pi.setheading(randrange(360))
pi.pendown()
pi.forward(PIN_LENGTH)
if crosses(x0, pi.xcor(), x_coordinates):
pi.undo()
pi.color(CROSSING_COLOR)
pi.dot(PINHEAD_SIZE)
pi.forward(PIN_LENGTH)
crossings += 1
pi.penup()
if previous_crossings != crossings:
estimate = (2 * PIN_LENGTH * pin) / (PLANK_WIDTH * crossings)
print(estimate)
previous_crossings = crossings
screen.exitonclick()
Now for the rest of the story. What the OP didn't mention is that this is a drawing of planks in a floor, and we're dropping pins onto it, keeping track of how many cross lines in the flooring, as a means of estimating the value of PI (π)!
Read about Buffon's needle for details. The gist is the probability of a pin crossing a line is a function of PI so we can turn the equation around, drop actual (or virtual) pins, to estimate PI.
The program outputs the running estimate for PI (π), based on pins dropped so far, to the console:
...
3.121212121212121
3.1215970961887476
3.1370772946859904
3.134418324291742
3.131768953068592
3.1381381381381384
3.1384892086330933
3.1358467983243568
3.1451612903225805
3.1454979129397733
3.1458333333333335
3.1491384432560903
3.1465005931198102
3.1438721136767316
3.144208037825059
3.144542772861357
3.1419316843345113

Loop patchwork in python3

I need to create a patchwork in Python 3. All I have left to do is create a loop which makes the design border the graphics window. I know I need a for loop however I am not sure how to do this.
This is what I have so far:
from graphics import *
def main():
height = eval(input("What is the height of the window"))
width = eval(input("What is the width of the window"))
colour = input("enter the colour of the patch")
win = GraphWin("Patch", 100*width, 100*height)
boat_x = 0
boat_y = 0
for x in range (4):
boat(win, boat_x, boat_y, colour)
boat_x = boat_x + 23
for i in range(height * 5):
boat(win, boat_x, boat_y, colour)
boat_x = boat_x + 24
for j in range(height * 5):
boat(win, boat_x, boat_y, colour)
boat_y = boat_y + 100
win.getMouse()
win.close()
def boat(win, x, y, colour):
body1 = Polygon(Point(1+x,95+y), Point(5+x,100+y),
Point(20+x,100+y), Point(24+x,95+y))
body1.draw(win)
line1 = Line(Point(13+x,95+y), Point(13+x,90+y))
line1.draw(win)
sail1 = Polygon(Point(1+x,90+y), Point(24+x,90+y), Point(13+x, 73+y))
sail1.setFill(colour)
sail1.draw(win)
body2 = Polygon(Point(1+x, 63), Point(5+x, 68),
Point(20+x,68), Point(24+x,63))
body2.draw(win)
line2 = Line(Point(13+x,63), Point(13+x,58))
line2.draw(win)
sail2 = Polygon(Point(1+x,58), Point(24+x, 58), Point(13+x,40))
sail2.setFill(colour)
sail2.draw(win)
body3 = Polygon(Point(1+x,28), Point(5+x,33),
Point(20+x,33), Point(24+x, 28))
body3.draw(win)
line3 = Polygon(Point(13+x,28), Point(13+x,23))
line3.draw(win)
sail3 = Polygon(Point(1+x,23), Point(24+x, 23), Point(13+x, 5))
sail3.setFill(colour)
sail3.draw(win)
main()
So far this creates the top border but nothing else.
I am also aware that the boat function isn't the most efficient way of drawing
When you say that you need to "make the design border the graphics window" I assume you want your boat design to be repeated several times along each edge of the window (that is, the top, bottom, left and right).
This should be doable in two loops. One will draw the top and bottom edges, the other two will draw the left and right edges. I'm not too sure how your drawing code works, so I'm guessing at some offsets here:
top = 0
bottom = (height-1) * 100
for x in range(0, width*100, 25):
boat(x, top, colour)
boat(x, bottom, colour)
left = 0
right = width * 100 - 25
for y in range(100, (height-1)*100, 100):
boat(left, y, colour)
boat(right, y, colour)
This should call your boat subroutine every 25 pixels across the top and bottom, and every 100 pixels along the left and right edges. Adjust the top, bottom, left and right values and the parameters in the range calls in the loops to make the spacing suit your needs (I just made it up). This code avoids drawing the corner items twice, though depending on how the drawing routine works that might not be necessary.

rotate text around its center in pycairo

community.
I know that there are many answers here, manuals, tutorials and references over the internets and amny more about this question. Also I know that knowledge of linear algebra is required.
But when I think about time to figuring out all the theory and solving exercises in practice - my head is blowing off and I can't do the simplest things :(
Please, if you know a little fast solution how to make rotation of text over its center before rendering it - tell me, pleeease.
For now I have:
#...
cr.move_to(*text_center)
myX, myY = text_center[0] - (width / 2), text_center[1] + (height / 2)
cr.save()
cr.translate(myX, myY)
cr.rotate(radians(text_angle))
cr.show_text(letter)
cr.restore()
#...
But my letter isn't rotating around itself. It's just like falling down to the right side :(
I know that my code isn't right. Maybe I miss transformation but I don't know how to make it right.
UPDATE: Unfortunately, text are not affected by translations, so
cr.translate(10000, 10000)
cr.rotate(radians(15))
cr.show_text("hello")
will be exactly the same as
cr.rotate(radians(15))
cr.show_text("hello")
And I don't know how to make text rotation over its center without making new surface or something (like new layer in graphic processor) :(
At least on the version of cairo available on my machine (1.8.8), the following approach works for me:
def text(ctx, string, pos, theta = 0.0, face = 'Georgia', font_size = 18):
ctx.save()
# build up an appropriate font
ctx.select_font_face(face , cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
ctx.set_font_size(font_size)
fascent, fdescent, fheight, fxadvance, fyadvance = ctx.font_extents()
x_off, y_off, tw, th = ctx.text_extents(string)[:4]
nx = -tw/2.0
ny = fheight/2
ctx.translate(pos[0], pos[1])
ctx.rotate(theta)
ctx.translate(nx, ny)
ctx.move_to(0,0)
ctx.show_text(string)
ctx.restore()
Which can be used in the following way:
width = 500
height = 500
surface = cairo.ImageSurface(cairo.FORMAT_RGB24, width, height)
ctx = cairo.Context(surface)
ctx.set_source_rgb(1,1,1)
rect(ctx, (0,0), (width, height), stroke=False)
ctx.set_source_rgb(0,0,0)
for i in xrange(5):
for j in xrange(5):
x = 100 * i + 20
y = 100 * j + 20
theta = math.pi*0.25*(5*i+j)
text(ctx, 'hello world', (x, y), theta, font_size=15)
surface.write_to_png('text-demo.png')
OK so cairo allows for text move_to and rotate. This means that what you want is to figure out (x,y) for move_to (T), such that when you rotate (R), the center point of your text is at your desired location, c=(cx,cy):
So you have to solve the equation Mv = c, where v is the text center relative to the text origin:
M = T*R
T = (1 0 x)
(0 1 y)
(0 0 1)
R = (cos r -sin r 0)
(sin r cos r 0)
(0 0 1)
v = (w/2, h', 1)
c = (cx, cy, 1)
h' = h/2 - (h - y_bearing)
Sanity checks:
when r is 0 (no rotation), you get x=cx-w/2, y=cy-h', which you know
is the correct answer
when r=-90 (text sideways, with "up" towards the right), you get what you expect,
ie x = cx - h' and y = cy + w/2
For python code, you will have to rewrite the above equation so you end up with A*t=b, where t=(x,y), and you will compute t = inv(A)*b. Then, you will simply do
cr.move_to(x, y)
cr.rotate(r)
cr.show_text(yourtext)
Note that the coordinate system in cairo has +y going down so there will be a couple signs to fix, and maybe y_bearing is not correct, but you get the idea.
Class function based on above input with multi-line text support.
def text(self, text, x, y, rotation=0, fontName="Arial", fontSize=10, verticalPadding=0):
rotation = rotation * math.pi / 180
self.ctx.select_font_face(fontName, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
self.ctx.set_font_size(fontSize)
fascent, fdescent, fheight, fxadvance, fyadvance = self.ctx.font_extents()
self.ctx.save()
self.ctx.translate(x, y)
self.ctx.rotate(rotation)
lines = text.split("\n")
for i in xrange(len(lines)):
line = lines[i]
xoff, yoff, textWidth, textHeight = self.ctx.text_extents(line)[:4]
offx = -textWidth / 2.0
offy = (fheight / 2.0) + (fheight + verticalPadding) * i
self.ctx.move_to(offx, offy)
self.ctx.show_text(line)
self.ctx.restore()
Should
myX, myY = text_center[0] + (height / 2), text_center[1] - (width / 2)
be
myX, myY = text_center[0] - (width / 2), text_center[1] + (height / 2)
?
That might explain why it's falling down to the right side.

Categories