How to draw concentric hexagons? - python-3.x

I need to draw concentric hexagons (4 to 10 in each set) randomly using Python turtle graphics (see image). I can draw random hexagons but can not make concentric ones:
import turtle
from random import randint
window = turtle.Screen()
window.bgcolor("yellow")
brad= turtle.Turtle()
brad.color("blue")
window.colormode(255)
def drawPoly(sideLen, noOfsides):
for i in range(noOfsides):
brad.forward(sideLen)
brad.left(360/noOfsides)
for i in range(20):
sideLen = randint(20,150)
xpos = randint(-200,200)
ypos = randint(-200,200)
brad.pensize(randint(1,3))
brad.pencolor(randint(1,255),randint(1,255),randint(1,255))
brad.penup()
brad.setpos(xpos,ypos)
brad.pendown()
drawPoly(sideLen,6)
window.exitonclick()
Here's a link to image
How can I make hexagons within a hexagon for 4 to 10 times and then move to the next hexagon?

The concept of an mcve applies to development as well as posting here. Start with a simple function or block of code that makes concentric hexagons without worrying about color, thickness, or style of the lines.
The key thing about hexagons is that the 'radius' (center to corners) is the same as the edge length (corner to corner). (Concentric pentagons, for instance, are harder, requiring some trig.) Assume you want two corners on the x axis. If the center is x,y, start at x+e,y at a rotation of 120 (where 0 is facing right). Given x, y, a list edges of radii-edge lengths, and turtle t in a pen-up state, the following should work.
for edge in edges:
t.setpos(x+e, y)
t.setheading(120)
t.pendown()
for i in range(6):
t.forward(e)
t.left(60)
t.penup()
EDIT: replace setangle with setheading, thanks to comment by cdlane.

Related

How to find the direction of triangles in an image using OpenCV

I am trying to find the direction of triangles in an image. below is the image:
These triangles are pointing upward/downward/leftward/rightward. This is not the actual image. I have already used canny edge detection to find edges then contours and then the dilated image is shown below.
My logic to find the direction:
The logic I am thinking to use is that among the three corner coordinates If I can identify the base coordinates of the triangle (having the same abscissa or ordinates values coordinates), I can make a base vector. Then angle between unit vectors and base vectors can be used to identify the direction. But this method can only determine if it is up/down or left/right but cannot differentiate between up and down or right and left. I tried to find the corners using cv2.goodFeaturesToTrack but as I know it's giving only the 3 most effective points in the entire image. So I am wondering if there is other way to find the direction of triangles.
Here is my code in python to differentiate between the triangle/square and circle:
#blue_masking
mask_blue=np.copy(img1)
row,columns=mask_blue.shape
for i in range(0,row):
for j in range(0,columns):
if (mask_blue[i][j]==25):
mask_blue[i][j]=255
else:
mask_blue[i][j]=0
blue_edges = cv2.Canny(mask_blue,10,10)
kernel_blue = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(2,2))
dilated_blue = cv2.dilate(blue_edges, kernel)
blue_contours,hierarchy =
cv2.findContours(dilated_blue,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
for cnt in blue_contours:
area = cv2.contourArea(cnt)
perimeter = cv2.arcLength(cnt,True)
M = cv2.moments(cnt)
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
if(12<(perimeter*perimeter)/area<14.8):
shape="circle"
elif(14.8<(perimeter*perimeter)/area<18):
shape="squarer"
elif(18<(perimeter*perimeter)/area and area>200):
shape="triangle"
print(shape)
print(area)
print((perimeter*perimeter)/area,"\n")
cv2.imshow('mask_blue',dilated_blue)
cv2.waitKey(0)
cv2.destroyAllWindows()
Source image can be found here: img1
Please help, how can I found the direction of triangles?
Thank you.
Assuming that you only have four cases: [up, down, left, right], this code should work well for you.
The idea is simple:
Get the bounding rectangle for your contour. Use: box = cv2.boundingRect(contour_pnts)
Crop the image using the bounding rectangle.
Reduce the image vertically and horizontally using the Sum option. Now you have the sum of pixels along each axis. The axis with the largest sum determines whether the triangle base is vertical or horizontal.
To identify whether the triangle is pointing left/right or up/down: you need to check whether the bounding rectangle center is before or after the max col/row:
The code (assumes you start from the cropped image):
ver_reduce = cv2.reduce(img, 0, cv2.REDUCE_SUM, None, cv2.CV_32F)
hor_reduce = cv2.reduce(img, 1, cv2.REDUCE_SUM, None, cv2.CV_32F)
#For smoothing the reduced vector, could be removed
ver_reduce = cv2.GaussianBlur(ver_reduce, (3, 1), 0)
hor_reduce = cv2.GaussianBlur(hor_reduce, (1, 3), 0)
_,ver_max, _, ver_col = cv2.minMaxLoc(ver_reduce)
_,hor_max, _, hor_row = cv2.minMaxLoc(hor_reduce)
ver_col = ver_col[0]
hor_row = hor_row[1]
contour_pnts = cv2.findNonZero(img) #in my code I do not have the original contour points
rect_center, size, angle = cv2.minAreaRect(contour_pnts )
print(rect_center)
if ver_max > hor_max:
if rect_center[0] > ver_col:
print ('right')
else:
print ('left')
else:
if rect_center[1] > hor_row:
print ('down')
else:
print ('up')
Photos:
Well, Mark has mentioned a solution that may not be as efficient but perhaps more accurate. I think this one should be equally efficient but perhaps less accurate. But since you already have a code that finds triangles, try adding the following code after you have found triangle contour:
hull = cv2.convexHull(cnt) # convex hull of contour
hull = cv2.approxPolyDP(hull,0.1*cv2.arcLength(hull,True),True)
# You can double check if the contour is a triangle here
# by something like len(hull) == 3
You should get 3 hull points for a triangle, these should be the 3 vertices of your triangles. Given your triangles always 'face' only in 4 directions; Y coordinate of the hull will have close value to the Y coordinate of the centroid for triangle facing left or right and whether it's pointing left or right will depend on whether hull X is less than or greater than centroid X. Similarly use hull and centroid X and Y for triangle pointing up or down.

2D plot of x,y,z points in Python without Matplotlib

I have a set of 3d points (I generate the positions of planets and moons in a stellar system from Keplers equations) I have the coordinates of all points as x,y,z, where the central star is 0,0,0. The code to produce the points works perfectly.
However, at the moment I plot a visualisation of this system from above - so I just ignore the z component for the purposes of visualisation and plot the x and y to the canvas as-is. This works as intended.
How would I generate x and y coordinates for plotting to the canvas that take into account the z coordinate, so that I can plot a view from another angle apart from directly above?
The only library I can use apart from the standard one would be numpy. I cannot use Matplotlib.
edit thanks to the comments I can now clarify with some psudocode.
Assume I have a bunch of points that have an xyz position.
What I currently do:
canvas.plot(point.x)
canvas.plot(point.y)
ignoring point z - so that it is as if all z's are 0 and it is viewed from 'above'
So that I can use my current plotting code - which takes into account scale and offsets to do with the canvas, I need new x and y coordinates that are as if the view is from another angle other than 'above'.
It seems from the helpful comments what I have to do is rotate the whole coordinate system so that it has a new z axis that is a result of a rotation of the whole system about the x and y axis.
Something like the following psudocode would do.
def rotate_about_axis(x_rotation_degrees, y_rotation_degrees, point.x, point.y, point.z):
new_plot_x = canvas_point_to_plot after magic code to rotate coordinates x_rotation_degrees about x axis
new_plot_y = canvas_point_to_plot after magic code to rotate coordinates y_rotation_degrees about y axis
return new_plot_x, new_plot_y
Then I could apply this to all the points I plot.
How would I do this in python?
I have come up with an answer, I hope it helps someone.
import numpy, math
def rotate_about_axis(x_rotation_degrees, y_rotation_degrees, point_x, point_y, point_z):
xrads = math.radians(x_rotation_degrees)
yrads = math.radians(y_rotation_degrees)
rotation = [xrads, yrads, 0]
rotation_angle = numpy.linalg.norm(rotation)
norm_rotation = rotation / numpy.linalg.norm(rotation)
base_points = [point_x, point_y, point_z]
points = numpy.dot(base_points, norm_rotation) * norm_rotation
points_difference = base_points - points
points_transform = numpy.cross(norm_rotation, base_points)
rotated_points = points + points_difference * numpy.cos(rotation_angle) + points_transform * numpy.sin(rotation_angle)
rotated_point_x = rotated_points[0]
rotated_point_y = rotated_points[1]
return(rotated_point_x, rotated_point_y)

Is there any way to scale the markers of a pyplot.scatter graph with respect to its axes length?

I am trying to plot a bunch of particles in a box. I want the partile sizes to be scaled according to the axes length. If the particles have a radius of 1 and the box of length of 100, how do I draw this using matplotlib.pyplot.scatter()
Attempt:
I have intialised the positions of the particles, such that none overlap each other.
When I try to plot these using pyplot.scatter(), I find that the particle sizes(radius)are not 0.01 times the box dimesions.
How do I do this?
I have attached a picture here.
If I change the box length from 100 to 1000, I expect to see the markers get their radius decrease by a factor of 10.
I'm using:
import matplotlib.pyplot
pyplot.scatter(particles_np[:,0],particles_np[:,1])
pyplot.show()`
particles_np is a numpy array which has the x and y positions of the particles.

itext7 how to consider rotation angle in TextPlusYExtractionStrategy

The text positioning (y) is determined by the TextPlusYExtractionStrategy perfectly when the rotation angle is 0 (portrait). In case of rotation angle is 90 degree (landscape) the position y provided from left to right instead of top to bottom. How to consider the rotation to get the text position y in when the page is 90 degree.
The below code helped me to resolve my issue.
if(rotation == 90){
chunkY = chunk.getLocation().getStartLocation().get(Vector.I1);
}
Constructor of TextPlusYExtractionStrategy can take the rotation angle, and use the Vector.I1 which is x co-ordinate of the chunk.
The issue will solve only the landscape (rotation=90) issue to find the Y coordinate of the search text.

Python 3 Tkinter canvas

I am trying to draw a clock that works. I am using a 600x600 form. I cant' figure out how to place the oval in the center of the form or how to add the minutes or the seconds tick marks inside the oval. I tried dash but couldn't get it to look right. Any suggestions. Thanks in advance.
This is what I have done so far:
from tkinter import *
canvas_width = 600
canvas_height = 600
master = Tk()
w = Canvas(master, width = canvas_width, height = canvas_height)
w.pack()
oval = w.create_oval(75,75,500,500)
minline = w.create_line(150,150,300,300)
mainloop()
The center of a drawn shape is the middle of the two points specified when it is drawn. Currently, the middle point of your shape (draw from 75, 75 to 500, 500) is 237.5, so, if you want the middle of it to be the middle of your page, and keep the 75, 75 coordinate, you would have to make the other one 525, 525 to completely mirror the first.
As for drawing the shape, you'll need some math in python, so I would first suggest doing an image as the background for the clock, so that less objects are drawn. But, if you must do it without other images, you must first import the math library.
import math
Now, for a mathematic principle: Any point on the circle of radius r can be expressed as the point (r*cosθ), (r*sinθ), where θ is the angle from the center to the point. The reason this is important is that you want each line on the side of the clock face to be pointing towards the center of the circle. To do that, we need the two points to draw the line on that together point towards the center, and fortunately for us this means that both points on the line are on different circles (our circle and one within it) but are at the same angle from the center.
Since we want 12 hour points around the circle, and 4 minute points between each of those (so 60 points in total), and 360 degrees in a circle (so 1 point for every 6 degrees), we will need a for loop that goes through that.
for angle in range(0, 360, 6):
Then we'll want 3 constants: One for the radius of the exterior circle (for the points to begin from), one for an interior circle (for the minute points to add at), and one for an even more interior circle (for the hour points to end at). We'll also want it to choose the more interior radius only every 30 degrees (because it appears every 5 points, and there are 6 degrees between them).
radius_out = 225
radius_in = 0 #temporary value
if (angle % 30) == 0: #the % symbol checks for remainder
radius_in = 210
else:
radius_in = 220
Now, for the conversion into radians (As math in python needs radians for sin and cos):
radians = (angle / 180) * math.pi
Next off, assigning the coordinates to variables so it's easier to read.
x_out = (radius_out * math.cos(radians)) + 300
y_out = (radius_out * math.sin(radians)) + 300
x_in = (radius_in * math.cos(radians)) + 300
y_in = (radius_in * math.sin(radians)) + 300
#the (+ 300) moves each point from a relative center of 0,0 to 300,300
And finally we assign it to a list so we can access it later if we need to. Make sure to define this list earlier outside of the for loop.
coords.append( w.create_line(x_out, y_out, x_in, y_in) )
This should give you your clock lines.
NOTE: Due to the way tkinter assigns x and y coordinates, this will draw lines from the 3 hour line clockwise back to it.
Hope this was helpful! If there is anything you don't understand, comment it below.

Resources