How to plot overlapping squares with similar size? - python

I try to plot a pattern with overlapping squares using matplotlib.pyplot. I am not sure how to get all squares of the same size.
Here is my code:
import matplotlib.pyplot as plt
plt.plot([2.2,2.2,3.5,3.5,2.2],[4.1,2.5,2.5,4.1,4.1],linestyle='solid',color="red")
plt.plot([3.2,4.2,4.2,3.2,3.2],[3.2,3.2,4.5,4.5,3.2],linestyle='solid',color="blue")
plt.plot([2.5,2.5,3.8,3.8,2.5],[1.8,3.8,3.8,1.8,1.8],linestyle='solid',color="green")
plt.plot([3.6,4.8,4.8,4.8,3.6,3.6],[2.2,2.2,0,0,0,2.2],linestyle='solid',color="black")
plt.title('square pattern')

Here is some code to draw 4 squares of the same size. The squares are represented by their lower left position. You can change these coordinates to easily position each square.
import matplotlib.pyplot as plt
from math import sqrt
colors = [ 'crimson', 'dodgerblue', 'limegreen', 'black']
side = 2
square1 = [1, 1.3]
square2 = [2.1, 0.5]
square3 = [1.3, 1.6]
square4 = [1.7, 2.8]
squares = [square1, square2, square3, square4]
for square, col in zip(squares, colors):
x = square[0]
y = square[1]
plt.plot([x, x + side, x + side, x, x],
[y, y, y + side, y + side, y],
color=col, linestyle='solid')
plt.title('square pattern')
plt.gca().set_aspect('equal')
plt.show()

Here is a way to get squares of all the same size, with the sum of the area of the rectangles equal to the sum of the areas of the squares. And with each square having the same center as the rectangle.
The area of each rectangle is width x height. Dividing the sum of the areas by the number of rectangles finds the average area. The side of a square with this area is its square root.
Code to calculate and draw the squares. The original rectangles are drawn lighter for comparison.
import matplotlib.pyplot as plt
from math import sqrt
colors = [ 'crimson', 'dodgerblue', 'limegreen', 'black']
rect1 = [[2.2,2.2,3.5,3.5,2.2],[4.1,2.5,2.5,4.1,4.1]]
rect2 = [[3.2,4.2,4.2,3.2,3.2],[3.2,3.2,4.5,4.5,3.2]]
rect3 = [[2.5,2.5,3.8,3.8,2.5],[1.8,3.8,3.8,1.8,1.8]]
rect4 = [[3.6,4.8,4.8,4.8,3.6,3.6],[2.2,2.2,0,0,0,2.2]]
rects = [rect1, rect2, rect3, rect4]
total_area = 0
for rect in rects:
width = max(rect[0]) - min(rect[0])
height = max(rect[1]) - min(rect[1])
total_area += width * height
square_side = sqrt(total_area / len(rects))
half = square_side / 2
for rect, col in zip(rects, colors):
center_x = (max(rect[0]) + min(rect[0])) / 2
center_y = (max(rect[1]) + min(rect[1])) / 2
plt.plot(rect[0], rect[1], color=col, linestyle='solid', alpha=0.2)
plt.plot([center_x - half, center_x - half, center_x + half, center_x + half, center_x - half],
[center_y + half, center_y - half, center_y - half, center_y + half, center_y + half],
color=col, linestyle='solid')
plt.title('square pattern')
plt.gca().set_aspect('equal')
plt.show()

Related

Draw a grid of cells using Matplotlib

I'm trying to draw a grid of cells using Matplotlib where each border (top, right, bottom, left) of a cell can have a different width (random number between 1 and 5). I should note also that the width and height of the inner area of a cell (white part) can vary between 15 and 20.
I want to know how to get the coordinates of each cell in order to avoid any extra space between the cells.
I tried several ideas however I did not get the right coordinates.
You could draw thin rectangles with a random thickness, in horizontal and vertical orientation to simulate the edges of the cells:
import matplotlib.pyplot as plt
import random
fig, ax = plt.subplots()
color = 'darkgreen'
size = 20
m, n = 4, 3
for i in range(m + 1):
for j in range(n + 1):
if j < n: # thick vertical line
w1 = random.randint(0, 5)
w2 = random.randint(w1 + 1, 6)
ax.add_patch(plt.Rectangle((i * size - w1, j * size), w2, size, color=color, lw=0))
if i < m: # thick horizontal line
h1 = random.randint(0, 5)
h2 = random.randint(h1 + 1, 6)
ax.add_patch(plt.Rectangle((i * size, j * size - h1), size, h2, color=color, lw=0))
ax.autoscale() # fit all rectangles into the view
ax.axis('off') # hide the surrounding axes
plt.tight_layout()
plt.show()

Fill area between two vectors in matlibplot

The following code calculates the following vectors:
the orientation Vector (red) and two vectors (blues) which result by rotating the red vector 60 degree clock -und counterclockwise.
import matplotlib.pyplot as plt
import numpy as np
def Visualize(orienVector,vector1,vector2):
# Create figure and subplot
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
# Plot data points
#ax.scatter(vector1[0], vector1[1], color='blue')
#ax.scatter(vector2[0], vector2[1], color='orange')
# Set limits for x and y axes
plt.xlim(-1, 1)
plt.ylim(-1, 1)
# ===== Important bits start here =====
# Set properties of spines
ax.spines['top'].set_color('none')
ax.spines['left'].set_position('zero')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position('zero')
# Set axis tick positions
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
# Set specific tick locations
ax.set_xticks([-10,-5, 0, 5 , 10])
ax.set_yticks([-10,-5, 0, 5 , 10])
# ===== End Of Important Bits =====
# Draw arrows
ax.arrow(0, 0 , vector1[0][0], vector1[1][0],
head_width=0.03,
head_length=0.1,
lw=1,
fc='blue',
ec='blue',
length_includes_head=True)
ax.arrow(0, 0 , vector2[0][0], vector2[1][0],
head_width=0.03,
head_length=0.1,
lw=1,
fc='blue',
ec='blue',
length_includes_head=True)
ax.arrow(0, 0 , orienVector[0][0], orienVector[1][0],
head_width=0.03,
head_length=0.1,
lw=2,
fc='red',
ec='red',
length_includes_head=True)
plt.show()
# rotation matrix clockwise
def rotMatrixClockWise(angle):
c, s = np.cos(angle), np.sin(angle)
R = np.array([[c, -s], [s, c]])
return R
# rotation matrix clockwise
def rotMatrixCounterClockWise(angle):
c, s = np.cos(angle), np.sin(angle)
R = np.array([[c, s], [-s, c]])
return R
# center of the poit of interests POIS
POI_X = [10,12,15,17,20,50]
POI_Y = [20,30,25,22,19,35]
# position of the pedestrian
pedPostion = np.array([[3],[4]])
# range of the horisontal angel view
spanningAngle = np.radians(60)
# calculate the cone
for angle in range(0,360,5):
# calculating the component of orientation vector V0, where the length |V0| = 1
x0 = 5*np.cos(np.radians(angle))
y0 = 5*np.sin(np.radians(angle))
v0 = np.array([[x0],[y0]])
v1 = rotMatrixCounterClockWise(spanningAngle).dot(v0)
v2 = rotMatrixClockWise(spanningAngle).dot(v0)
Visualize(v0,v1,v2)
The output of this vector looks like
I'm trying to fill the area between the blue vectors to obtain a cone like the following:
The distance between the head of the cone (0,0) and the arc is always 5
However, I can't get it to work. I'm new to Matlibplot
Maybe not completely what you are looking for, but you could create a wedge between the vectors. It would be more appropriate to fit an ellipse and fill the coordinates in between. Here is an example for the wedge.
import matplotlib.pyplot as plt, numpy as np
from matplotlib import patches
v1 = np.array((1, 2))
v2 = np.array((1, -2))
base = np.array((0, 0))
theta1 = np.rad2deg(np.arctan(v1[1] / v1[0]))
theta2 = np.rad2deg(np.arctan(v2[1] / v2[0]))
fig, ax = plt.subplots()
a, b = theta1, theta2
if b > a:
a, b = b, a
artist = patches.Arc(base,
width = 1,
height = 1,
theta1 = a,
theta2 = b,
color = 'green')
ax.arrow(*base, *v1, color = 'black')
ax.arrow(*base, *v2, color = 'black')
ax.arrow(*base, *(v2 + v1), color = 'red')
wedge = patches.Wedge(base,
r = 1,
width = 1,
theta1 = theta2,
theta2 = theta1,
color = 'green')
ax.add_patch(wedge)
fig.show()
You could figure out analytically the points in the arc (a portion of a circle, so y = +- (1-x^2)**0.5), add them to the polygon defined by the origin and extremities of red and yellow vectors, then use https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.fill.html.
An example of filling a polygon is here: https://matplotlib.org/stable/gallery/lines_bars_and_markers/fill.html#sphx-glr-gallery-lines-bars-and-markers-fill-py

How to show an arc with an angle of a line wrt x axis in python [duplicate]

This question already has answers here:
Best way to plot an angle between two lines in Matplotlib
(5 answers)
Closed 3 years ago.
This is my code for making lines on a graph in a xy plane. I want to show an arc for each line with angle representing on it.
sim_score =[0.993832,0.543218,0.234745 ,0.873513,0.234565,0.789212]
plt.figure()
for i in sim_score:
for j in sim_label:
a= math.acos(i)
b = a * 180 / math.pi
point=(0,0)
x,y = point
length=10
# find the end point
endy = length * math.sin(math.radians(b))
endx = length * math.cos(math.radians(b))
# plot the points
ax = plt.subplot(111)
# set the bounds to be 10, 10
ax.set_ylim([0, 10])
ax.set_xlim([0, 10])
ax.plot([x, endx], [y,endy] )
I want to make arc with angle representing on it as a label.
following this post, i have adapted the code source to have a good basis:
import matplotlib.pyplot as plt
from matplotlib.patches import Arc
from matplotlib.lines import Line2D
import math
def get_angle_text(angle_plot):
angle = angle_plot.get_label()[:-1] # Excluding the degree symbol
angle = "%0.2f" % float(angle) + u"\u00b0" # Display angle upto 2 decimal places
# Set the label position
x_width = angle_plot.width / 2
y_width = math.sin(math.radians(angle_plot.theta2))
return [x_width, y_width, angle]
def get_angle_plot(line1, offset=1, color=None, origin=[0, 0], len_x_axis=1, len_y_axis=1):
l1xy = line1.get_xydata()
# Angle between line1 and x-axis
slope = (l1xy[1][1] - l1xy[0][1]) / float(l1xy[1][0] - l1xy[0][0])
angle = abs(math.degrees(math.atan(slope))) # Taking only the positive angle
if color is None:
color = line1.get_color() # Uses the color of line 1 if color parameter is not passed.
return Arc(origin, len_x_axis * offset, len_y_axis * offset, 0, 0, angle, color=color,
label=str(angle) + u"\u00b0")
sim_score = [0.993832, 0.543218, 0.234745, 0.873513]
fig = plt.figure()
ax = fig.add_subplot(111)
offset = 0
for i in sim_score:
offset += 2 # you play with this value to draw arc spaced from the previous
a = math.acos(i)
b = a * 180 / math.pi
point = (0, 0)
x, y = point
length = 10
# find the end point
endy = length * math.sin(math.radians(b))
endx = length * math.cos(math.radians(b))
line = Line2D([x, endx], [y, endy], linewidth=1, linestyle="-", color="blue")
angle_plot = get_angle_plot(line, offset)
angle_text = get_angle_text(angle_plot)
ax.add_line(line )
ax.add_patch(angle_plot)
ax.text(*angle_text) # To display the angle value
ax.set_ylim([0, 10])
ax.set_xlim([0, 10])
plt.show()
you should adapt the program to place the label, i have just done a quick calculus of position
graph result:

plot a circle with some colored points in python

I want to plot a circle with specified center position and radius and plot 100 random points between [0-500] which 80% of them in circle and 20% around with different colors.
To solve this problem I used Plot circle that contains 80% (x, y) points and customized based on my requirements but it's not working.
import numpy as np
import matplotlib.pyplot as plt
n = 100
low = 0
high = 500
x = np.random.random_integers(low, high, n)
y = np.random.random_integers(low, high, n)
x0 = y0 = 250
r = 200
#t = 80 # percent
#r0 = np.percentile(r, t)
plt.plot(x, y, '.')
circle = plt.Circle((x0, y0), r, color='black', fill=False, linestyle='--')
plt.plot(x0, y0, color='black', marker='^')
plt.gca().add_artist(circle)
plt.axis([0, 500, 0, 500])
plt.show()
I found the answer of my question.
we can use the equation of circle to check the point is in the circle or not.
(x-x0)**2+(y-y0)**2 < r**2
center:(x0, y0)
radius:r

how to make arrow that loops in matplotlib?

what is the right way to draw an arrow that loops back to point to its origin in matplotlib? i tried:
plt.figure()
plt.xlim([0, 1])
plt.ylim([0, 1])
plt.annotate("", xy=(0.6, 0.9),
xycoords="figure fraction",
xytext = (0.6, 0.8),
textcoords="figure fraction",
fontsize = 10, \
color = "k",
arrowprops=dict(edgecolor='black',
connectionstyle="angle,angleA=-180,angleB=45",
arrowstyle = '<|-',
facecolor="k",
linewidth=1,
shrinkA = 0,
shrinkB = 0))
plt.show()
this doesn't give the right result:
the connectionstyle arguments are hard to follow from this page (http://matplotlib.org/users/annotations_guide.html).
i'm looking for is something like this or this:
update: the answer linked to does not show how do this with plt.annotate which has other features i want to use. the proposal to use $\circlearrowleft$ marker is not a real solution.
It seems the easiest way to create an easily modifiable looping arrow is to use patches. I've pasted code to do this below. Change the variables in the variables section and things should all rotate and scale together. You can play around with the patch that creates the arrow head to make a different shape though I suspect that this triangle will be the easiest one.
%matplotlib inline
# from __future__ import division #Uncomment for python2.7
import matplotlib.pyplot as plt
from matplotlib.patches import Arc, RegularPolygon
import numpy as np
from numpy import radians as rad
fig = plt.figure(figsize=(9,9))
ax = plt.gca()
def drawCirc(ax,radius,centX,centY,angle_,theta2_,color_='black'):
#========Line
arc = Arc([centX,centY],radius,radius,angle=angle_,
theta1=0,theta2=theta2_,capstyle='round',linestyle='-',lw=10,color=color_)
ax.add_patch(arc)
#========Create the arrow head
endX=centX+(radius/2)*np.cos(rad(theta2_+angle_)) #Do trig to determine end position
endY=centY+(radius/2)*np.sin(rad(theta2_+angle_))
ax.add_patch( #Create triangle as arrow head
RegularPolygon(
(endX, endY), # (x,y)
3, # number of vertices
radius/9, # radius
rad(angle_+theta2_), # orientation
color=color_
)
)
ax.set_xlim([centX-radius,centY+radius]) and ax.set_ylim([centY-radius,centY+radius])
# Make sure you keep the axes scaled or else arrow will distort
drawCirc(ax,1,1,1,0,250)
drawCirc(ax,2,1,1,90,330,color_='blue')
plt.show()
I find no way to make a loop using plt.annotate only once, but using it four times works :
import matplotlib.pyplot as plt
fig,ax = plt.subplots()
# coordinates of the center of the loop
x_center = 0.5
y_center = 0.5
radius = 0.2
# linewidth of the arrow
linewidth = 1
ax.annotate("", (x_center + radius, y_center), (x_center, y_center + radius),
arrowprops=dict(arrowstyle="-",
shrinkA=10, # creates a gap between the start point and end point of the arrow
shrinkB=0,
linewidth=linewidth,
connectionstyle="angle,angleB=-90,angleA=180,rad=10"))
ax.annotate("", (x_center, y_center - radius), (x_center + radius, y_center),
arrowprops=dict(arrowstyle="-",
shrinkA=0,
shrinkB=0,
linewidth=linewidth,
connectionstyle="angle,angleB=180,angleA=-90,rad=10"))
ax.annotate("", (x_center - radius, y_center), (x_center, y_center - radius),
arrowprops=dict(arrowstyle="-",
shrinkA=0,
shrinkB=0,
linewidth=linewidth,
connectionstyle="angle,angleB=-90,angleA=180,rad=10"))
ax.annotate("", (x_center, y_center + radius), (x_center - radius, y_center),
arrowprops=dict(arrowstyle="-|>",
facecolor="k",
linewidth=linewidth,
shrinkA=0,
shrinkB=0,
connectionstyle="angle,angleB=180,angleA=-90,rad=10"))
plt.show()
Try this:
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.set_xlim(1,3)
ax.set_ylim(1,3)
ax.plot([2.5],[2.5],marker=r'$\circlearrowleft$',ms=100)
plt.show()
My suggestion uses just the plot command
import matplotlib.pyplot as plt
import numpy as np
def circarrowdraw(x0, y0, radius=1, aspect=1, direction=270, closingangle=-330,
arrowheadrelativesize=0.3, arrowheadopenangle=30, *args):
"""
Circular arrow drawing. x0 and y0 are the anchor points.
direction gives the angle of the circle center relative to the anchor
in degrees. closingangle indicates how much of the circle is drawn
in degrees with positive being counterclockwise and negative being
clockwise. aspect is important to make the aspect of the arrow
fit the current figure.
"""
xc = x0 + radius * np.cos(direction * np.pi / 180)
yc = y0 + aspect * radius * np.sin(direction * np.pi / 180)
headcorrectionangle = 5
if closingangle < 0:
step = -1
else:
step = 1
x = [xc + radius * np.cos((ang + 180 + direction) * np.pi / 180)
for ang in np.arange(0, closingangle, step)]
y = [yc + aspect * radius * np.sin((ang + 180 + direction) * np.pi / 180)
for ang in np.arange(0, closingangle, step)]
plt.plot(x, y, *args)
xlast = x[-1]
ylast = y[-1]
l = radius * arrowheadrelativesize
headangle = (direction + closingangle + (90 - headcorrectionangle) *
np.sign(closingangle))
x = [xlast +
l * np.cos((headangle + arrowheadopenangle) * np.pi / 180),
xlast,
xlast +
l * np.cos((headangle - arrowheadopenangle) * np.pi / 180)]
y = [ylast +
aspect * l * np.sin((headangle + arrowheadopenangle) * np.pi / 180),
ylast,
ylast +
aspect * l * np.sin((headangle - arrowheadopenangle) * np.pi / 180)]
plt.plot(x, y, *args)
To test it:
plt.figure()
plt.plot(np.arange(10)**2, 'b.')
bb = plt.gca().axis()
asp = (bb[3] - bb[2]) / (bb[1] - bb[0])
circarrowdraw(6, 36 , radius=0.4, aspect=asp, direction=90)
plt.grid()
plt.show()
Another possibility is to use tikz to generate the figure:
\documentclass {minimal}
\usepackage {tikz}
\begin{document}
\usetikzlibrary {arrows}
\begin {tikzpicture}[scale=1.8]
\draw[-angle 90, line width=5.0mm, rounded corners=20pt]
(0.25,0)-- (1.0, 0.0) -- (1.0, -3.0) -- (-3.0, -3.0) -- (-3.0, 0) --(-1,0);
\end{tikzpicture}
\end{document}
This is the result:
there is a pgf/tikz backend in matplotlib that you could generate your matplotlib output to tikz code that pdflatex or lualatex can process.
So this way, I think, you could insert seamlessly the looparrow figure in
your matplotlib figure.
See for ex:
http://matplotlib.org/users/whats_new.html#pgf-tikz-backend
#Aguy's answer is useful if you want a smooth arc instead of a complete circle. In Aguy's answer an arrow head is drawn line by line, but instead a FancyArrowPatch can be used. This gives a full arrow head, which might be more suitable. Below gives the code with the FancyArrowPatch arrow head.
def circarrowdraw(x0, y0, radius=1, aspect=1, direction=270, closingangle=-330, rotate_head = 0.0, color='b', *args):
"""
Circular arrow drawing. x0 and y0 are the anchor points.
direction gives the angle of the circle center relative to the anchor
in degrees. closingangle indicates how much of the circle is drawn
in degrees with positive being counterclockwise and negative being
clockwise. aspect is important to make the aspect of the arrow
fit the current figure. rotate_head is used to rotate the arrow head
by increasing the y value of the arrow's tail coordinate.
"""
# Center of circle
xc = x0 + radius * np.cos(direction * np.pi / 180)
yc = y0 + aspect * radius * np.sin(direction * np.pi / 180)
# Draw circle
if closingangle < 0:
step = -1
else:
step = 1
x = [xc + radius * np.cos((ang + 180 + direction) * np.pi / 180)
for ang in np.arange(0, closingangle, step)]
y = [yc + aspect * radius * np.sin((ang + 180 + direction) * np.pi / 180)
for ang in np.arange(0, closingangle, step)]
plt.plot(x, y, *args, color=color)
# Draw arrow head
arc_arrow_head = patches.FancyArrowPatch((x[-1], y[-1] + rotate_head),
(x[0], y[0]),
arrowstyle="Simple,head_width=10,head_length=10,tail_width=0.01",
color = color,
zorder = 10)
plt.gca().add_patch(arc_arrow_head)
To test it:
plt.plot([0, 0, 1, 1, 0], [0, 1, 1, 0, 0])
circarrowdraw(1.0, 1.0 , radius=0.1, aspect=0.3, direction=90, closingangle=-345, rotate_head = 0.003)
circarrowdraw(0.0, 1.0 , radius=0.1, aspect=1, direction=-90, closingangle=-345, rotate_head = 0.0)
circarrowdraw(0.0, 0.0 , radius=0.1, aspect=3.0, direction=90, closingangle=-345, rotate_head = 0.01)
circarrowdraw(1.0, 0.0 , radius=0.1, aspect=0.3, direction=-90, closingangle=-345)
plt.show()
Picture of image (I don't have a high enough reputation to embed the image in my answer)

Categories

Resources