Commit 5228edb8 authored by Baptiste Durand's avatar Baptiste Durand

Pass unit tests for geometry (except unexpected behavior for round_corner)

Change name of boolean operations;
Correction name of plot functions;
parent b506dedd
......@@ -96,7 +96,7 @@ macro_s = geo.PlaneSurface(macro_ll)
logger.info('Start boolean operations on surfaces')
pattern_s = [geo.PlaneSurface(ll) for ll in pattern_ll]
rve_s = geo.AbstractSurface.bool_cut(macro_s, pattern_s)
rve_s = geo.surface_bool_cut(macro_s, pattern_s)
rve_s = rve_s[0]
logger.info('Done boolean operations on surfaces')
rve_s_phy = geo.PhysicalGroup([rve_s], 2, "partition_plein")
......@@ -153,4 +153,4 @@ geo.PhysicalGroup.set_group_mesh(1)
gmsh.model.mesh.generate(2)
gmsh.write("%s.msh"%name)
os.system("gmsh %s.msh &" %name)
gmsh.fltk.run()
\ No newline at end of file
gmsh.fltk.run()
......@@ -19,35 +19,13 @@ import gmsh
import matplotlib.pyplot as plt
import numpy as np
from .curves import AbstractCurve, Arc, Line, bndry_logger
from .physical import PhysicalGroup
from .point import Point
from .surfaces import (
AbstractSurface,
LineLoop,
PlaneSurface,
surface_bool_cut,
surface_bool_intersect,
)
from .tools import (
angle_between,
bisector,
dual_base,
macro_line_fragments,
offset,
remove_duplicates,
round_corner,
unit_vect,
)
from .transformations import plane_reflection, point_reflection, rotation, translation
logger = logging.getLogger(__name__)
# nice shortcuts
model = gmsh.model
factory = model.occ
logger = logging.getLogger(__name__)
warnings.simplefilter("always")
# ? Doc: https://docs.python.org/3.6/library/warnings.html
......@@ -77,6 +55,29 @@ def set_gmsh_option(option, val):
logger.info(f"Gmsh option {option} set to {val} (previously : {preval}).")
from .curves import AbstractCurve, Arc, Line, bndry_logger
from .physical import PhysicalGroup
from .point import Point
from .surfaces import (
AbstractSurface,
LineLoop,
PlaneSurface,
surface_bool_cut,
surface_bool_intersect,
)
from .tools import (
angle_between,
bisector,
dual_base,
macro_line_fragments,
offset,
remove_duplicates,
round_corner,
unit_vect,
)
from .transformations import plane_reflection, point_reflection, rotation, translation
def init_geo_tools():
"""
The Gmsh Python API must be initialized before using any functions.
......@@ -112,7 +113,7 @@ __all__ = [
"logger",
"set_gmsh_option",
"init_geo_tools",
"reset"
"reset",
# * points
"Point",
# * curves
......
......@@ -49,7 +49,6 @@ class Curve(object):
self.def_pts = def_pts_list
self.tag = None
self.gmsh_constructor = gmsh_api_add_function
Curve.all_instances.append(self)
def __eq__(self, other):
"""
......@@ -160,9 +159,9 @@ class Arc(Curve):
"""Représenter l'arc de cercle dans un plot matplotlib.
Disponible seulement en 2D pour l'instant."""
self.def_pts[0].plot(end_pts_color, pt_size)
self.def_pts[2].plot(end_pts_color, pt_size)
self.def_pts[1].plot(center_color, pt_size)
self.def_pts[0].plot2D(end_pts_color, pt_size)
self.def_pts[2].plot2D(end_pts_color, pt_size)
self.def_pts[1].plot2D(center_color, pt_size)
circle = plt.Circle(
(self.def_pts[1].coord[0], self.def_pts[1].coord[1]),
self.radius,
......
......@@ -101,7 +101,7 @@ class LineLoop(object):
if not self.sides:
self.vertices_2_sides()
for elmt in self.sides:
elmt.plot(color)
elmt.plot2D(color)
def add_gmsh(self):
if self.tag:
......
......@@ -11,7 +11,7 @@ Perform diverse operations on numpy arrays, points or curves.
from . import np, plt, logger, math
from .point import Point
from .curves import Line, Arc
from .transformations import translation
import copy
......@@ -108,6 +108,7 @@ def round_corner(inp_pt, pt_amt, pt_avl, r, junction_raduis=False, plot=False):
Ces segments orientés relient les deux points en amont et aval de l'angle à l'arc.
"""
from .transformations import translation
# Direction en amont et en aval
v_amt = unit_vect(pt_amt.coord - inp_pt.coord)
v_avl = unit_vect(pt_avl.coord - inp_pt.coord)
......@@ -149,15 +150,15 @@ def round_corner(inp_pt, pt_amt, pt_avl, r, junction_raduis=False, plot=False):
colors = ["black", "red", "blue"]
x, y = list(), list()
for pt, color in zip(pts, colors):
pt.plot(color)
pt.plot2D(color)
x.append(pt.coord[0])
y.append(pt.coord[1])
pt_racc_amt.plot("black")
pt_racc_avl.plot("black")
pt_ctr.plot("purple")
racc_amt.plot("red")
racc_avl.plot("blue")
ax.add_patch(round_arc.plot("purple"))
pt_racc_amt.plot2D("black")
pt_racc_avl.plot2D("black")
pt_ctr.plot2D("purple")
racc_amt.plot2D("red")
racc_avl.plot2D("blue")
ax.add_patch(round_arc.plot2D("purple"))
plt.axis("equal")
plt.xlim(min(x), max(x))
plt.ylim(min(y), max(y))
......
......@@ -162,7 +162,7 @@ class Gmsh2DRVE(object):
logger.info("Start boolean operations on surfaces")
phy_surf = list()
pattern_s = [geo.PlaneSurface(ll) for ll in pattern_ll]
rve_s = geo.AbstractSurface.bool_cut(macro_s, pattern_s)
rve_s = geo.surface_bool_cut(macro_s, pattern_s)
if len(rve_s) == 1:
logger.info(
"The main material domain of the RVE is connected (topological property)."
......@@ -178,7 +178,7 @@ class Gmsh2DRVE(object):
rve_s_phy = geo.PhysicalGroup(rve_s, 2, "microstruct_domain")
phy_surf.append(rve_s_phy)
if soft_mat:
soft_s = geo.AbstractSurface.bool_cut(macro_s, rve_s)
soft_s = geo.surface_bool_cut(macro_s, rve_s)
soft_s_phy = geo.PhysicalGroup(soft_s, 2, "soft_domain")
phy_surf.append(soft_s_phy)
logger.info("Done boolean operations on surfaces")
......
......@@ -6,8 +6,7 @@ Created on Mon Oct 15 11:00:18 2018
"""
import os
import sys
import geometry as geo
import ho_homog.geometry as geo
import math
import numpy as np
import matplotlib.pyplot as plt
......@@ -21,31 +20,33 @@ import gmsh
model = gmsh.model
factory = model.occ
logger = logging.getLogger(__name__) #http://sametmax.com/ecrire-des-logs-en-python/
logger = logging.getLogger(__name__) # http://sametmax.com/ecrire-des-logs-en-python/
logger.setLevel(logging.INFO)
if __name__ == "__main__":
logger_root = logging.getLogger()
logger_root.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s :: %(levelname)s :: %(message)s') # Afficher le temps à chaque message
file_handler = RotatingFileHandler(f'activity_{__name__}.log', 'a', 1000000)
formatter = logging.Formatter("%(asctime)s :: %(levelname)s :: %(message)s")
# Afficher le temps à chaque message
file_handler = RotatingFileHandler(f"activity_{__name__}.log", "a", 1000000)
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(formatter)
logger_root.addHandler(file_handler) #Pour écriture d'un fichier log
formatter = logging.Formatter('%(levelname)s :: %(message)s')
logger_root.addHandler(file_handler) # Pour écriture d'un fichier log
formatter = logging.Formatter("%(levelname)s :: %(message)s")
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.INFO)
stream_handler.setFormatter(formatter)
logger_root.addHandler(stream_handler)
SR2 = math.sqrt(2.)
SR2 = math.sqrt(2.0)
# plt.ion() #* interactive mode
plt.ioff()
geo.init_geo_tools()
print(gmsh.option.getNumber('Mesh.CharacteristicLengthExtendFromBoundary'))
gmsh.option.setNumber('Mesh.CharacteristicLengthExtendFromBoundary',1)
print(gmsh.option.getNumber("Mesh.CharacteristicLengthExtendFromBoundary"))
gmsh.option.setNumber("Mesh.CharacteristicLengthExtendFromBoundary", 1)
print("matplotlib pyplot interactive mode : %s" % (plt.isinteractive()))
print("matplotlib pyplot interactive mode : %s" %(plt.isinteractive()))
def test_Point():
"""
......@@ -54,31 +55,32 @@ def test_Point():
name = "test_Point"
gmsh.model.add(name)
coords = [(0.,0.), (0.,2.5), (SR2,SR2)]
coords = [(0.0, 0.0), (0.0, 2.5), (SR2, SR2)]
coords = [np.array(c) for c in coords]
pts = [geo.Point(c) for c in coords]
print("Before calling the API addPoint function :")
for pt in pts :
for pt in pts:
print("Point tag : {}, coordinates : {}".format(pt.tag, pt.coord))
for pt in pts:
pt.add_gmsh()
print("After calling the API addPoint function :")
for pt in pts :
for pt in pts:
print("Point tag : {}, coordinates : {}".format(pt.tag, pt.coord))
factory.synchronize()
data = gmsh.model.getEntities()
print("model name : %s"%name)
print("model name : %s" % name)
print(data)
fig = plt.figure()
plt.axis('equal')
plt.figure()
plt.axis("equal")
for pt in pts:
pt.plot()
pt.plot2D()
plt.pause(0.1)
gmsh.write("%s.brep"%name)
os.system("gmsh %s.brep &" %name)
gmsh.write("%s.brep" % name)
os.system("gmsh %s.brep &" % name)
def test_Line():
"""
......@@ -91,30 +93,31 @@ def test_Line():
name = "test_Line"
gmsh.model.add(name)
coords = [(0.,0.), (0.,2.5), (SR2, SR2)]
coords = [(0.0, 0.0), (0.0, 2.5), (SR2, SR2)]
mesh_size = [0.1, 0.1, 0.01]
coords = [np.array(c) for c in coords]
pts = [geo.Point(c, m_s) for c, m_s in zip(coords, mesh_size)]
lines = [geo.Line(pts[0], pts[1]),geo.Line(pts[0], pts[2])]
lines = [geo.Line(pts[0], pts[1]), geo.Line(pts[0], pts[2])]
for ln in lines:
ln.add_gmsh()
factory.synchronize()
data = gmsh.model.getEntities()
print("model name : %s \n "%name, data)
gmsh.model.mesh.generate(1) #We can generatate 1D meshes for the 2 lines
print("model name : %s \n " % name, data)
gmsh.model.mesh.generate(1) # We can generatate 1D meshes for the 2 lines
fig = plt.figure()
plt.axis('equal')
plt.figure()
plt.axis("equal")
for ln in lines:
ln.plot()
ln.plot2D()
plt.pause(0.1)
gmsh.model.mesh.generate(1)
gmsh.write("%s.brep"%name)
gmsh.write("%s.msh"%name)
os.system("gmsh %s.brep &" %name)
os.system("gmsh %s.msh &" %name)
gmsh.write("%s.brep" % name)
gmsh.write("%s.msh" % name)
os.system("gmsh %s.brep &" % name)
os.system("gmsh %s.msh &" % name)
def test_Arc():
"""
......@@ -126,13 +129,32 @@ def test_Arc():
name = "test_Arc"
gmsh.model.add(name)
coords = [(0.05,0.), (1.8,0.), (2.0, 0.2), (2.0, 1.95), (1.95, 2.0), (0.2, 2.0), (0., 1.8), (0.,0.05)]
mesh_size = [0.01]*len(coords)
coords = [
(0.05, 0.0),
(1.8, 0.0),
(2.0, 0.2),
(2.0, 1.95),
(1.95, 2.0),
(0.2, 2.0),
(0.0, 1.8),
(0.0, 0.05),
]
mesh_size = [0.01] * len(coords)
pts = [geo.Point(np.array(c), m_s) for c, m_s in zip(coords, mesh_size)]
lines = [geo.Line(pts[0], pts[1]), geo.Line(pts[2], pts[3]), geo.Line(pts[4], pts[5]), geo.Line(pts[6], pts[7])]
centers = [(1.8,0.2), (1.95, 1.95), (0.2, 1.8), (0.05,0.05)]
lines = [
geo.Line(pts[0], pts[1]),
geo.Line(pts[2], pts[3]),
geo.Line(pts[4], pts[5]),
geo.Line(pts[6], pts[7]),
]
centers = [(1.8, 0.2), (1.95, 1.95), (0.2, 1.8), (0.05, 0.05)]
centers = [geo.Point(np.array(c)) for c in centers]
arcs = [geo.Arc(pts[1], centers[0], pts[2]),geo.Arc(pts[3], centers[1], pts[4]), geo.Arc(pts[5], centers[2], pts[6]), geo.Arc(pts[7], centers[3], pts[0])]
arcs = [
geo.Arc(pts[1], centers[0], pts[2]),
geo.Arc(pts[3], centers[1], pts[4]),
geo.Arc(pts[5], centers[2], pts[6]),
geo.Arc(pts[7], centers[3], pts[0]),
]
for ln in lines:
ln.add_gmsh()
for arc in arcs:
......@@ -140,23 +162,24 @@ def test_Arc():
factory.synchronize()
data = gmsh.model.getEntities()
print("model name : %s \n"%name, data)
gmsh.model.mesh.generate(1) #We can generatate 1D meshes for the 2 lines
print("model name : %s \n" % name, data)
gmsh.model.mesh.generate(1) # We can generatate 1D meshes for the 2 lines
fig, ax = plt.subplots()
plt.axis('equal')
plt.axis("equal")
for pt in pts:
pt.plot()
pt.plot2D()
for ln in lines:
ln.plot()
ln.plot2D()
for arc in arcs:
arc.plot()
arc.plot2D()
plt.pause(0.1)
gmsh.model.mesh.generate(1)
gmsh.write("%s.brep"%name)
gmsh.write("%s.msh"%name)
os.system("gmsh %s.brep &" %name)
os.system("gmsh %s.msh &" %name)
gmsh.write("%s.brep" % name)
gmsh.write("%s.msh" % name)
os.system("gmsh %s.brep &" % name)
os.system("gmsh %s.msh &" % name)
def test_LineLoop():
"""
......@@ -170,21 +193,40 @@ def test_LineLoop():
name = "test_LineLoop"
gmsh.model.add(name)
#* Explicit constructor
coords = [(0.05,0.), (1.8,0.), (2.0, 0.2), (2.0, 1.95), (1.95, 2.0), (0.2, 2.0), (0., 1.8), (0.,0.05)]
mesh_size = [0.01]*len(coords)
# * Explicit constructor
coords = [
(0.05, 0.0),
(1.8, 0.0),
(2.0, 0.2),
(2.0, 1.95),
(1.95, 2.0),
(0.2, 2.0),
(0.0, 1.8),
(0.0, 0.05),
]
mesh_size = [0.01] * len(coords)
pts = [geo.Point(np.array(c), m_s) for c, m_s in zip(coords, mesh_size)]
lines = [geo.Line(pts[0], pts[1]), geo.Line(pts[2], pts[3]), geo.Line(pts[4], pts[5]), geo.Line(pts[6], pts[7])]
centers = [(1.8,0.2), (1.95, 1.95), (0.2, 1.8), (0.05,0.05)]
lines = [
geo.Line(pts[0], pts[1]),
geo.Line(pts[2], pts[3]),
geo.Line(pts[4], pts[5]),
geo.Line(pts[6], pts[7]),
]
centers = [(1.8, 0.2), (1.95, 1.95), (0.2, 1.8), (0.05, 0.05)]
centers = [geo.Point(np.array(c)) for c in centers]
arcs = [geo.Arc(pts[1], centers[0], pts[2]),geo.Arc(pts[3], centers[1], pts[4]), geo.Arc(pts[5], centers[2], pts[6]), geo.Arc(pts[7], centers[3], pts[0])]
arcs = [
geo.Arc(pts[1], centers[0], pts[2]),
geo.Arc(pts[3], centers[1], pts[4]),
geo.Arc(pts[5], centers[2], pts[6]),
geo.Arc(pts[7], centers[3], pts[0]),
]
elmts_1D = [item for pair in zip(lines, arcs) for item in pair]
ll_1 = geo.LineLoop(elmts_1D, explicit=True)
#* Implicit constructor
coords = [(1.,1.), (3., 1.), (3., 3.), (1., 3.)]
mesh_size = [0.01]*len(coords)
# * Implicit constructor
coords = [(1.0, 1.0), (3.0, 1.0), (3.0, 3.0), (1.0, 3.0)]
mesh_size = [0.01] * len(coords)
vertc = [geo.Point(np.array(c), m_s) for c, m_s in zip(coords, mesh_size)]
ll_2 = geo.LineLoop(vertc, explicit=False)
......@@ -192,19 +234,20 @@ def test_LineLoop():
ll_2.add_gmsh()
factory.synchronize()
data = gmsh.model.getEntities()
print("model name : %s \n"%name, data)
gmsh.model.mesh.generate(1) #We can generatate 1D meshes for the 2 lines
print("model name : %s \n" % name, data)
gmsh.model.mesh.generate(1) # We can generatate 1D meshes for the 2 lines
fig, ax = plt.subplots()
plt.axis('equal')
ll_1.plot()
ll_2.plot()
plt.axis("equal")
ll_1.plot2D()
ll_2.plot2D()
plt.pause(0.1)
gmsh.model.mesh.generate(1)
gmsh.write("%s.brep"%name)
gmsh.write("%s.msh"%name)
os.system("gmsh %s.brep &" %name)
os.system("gmsh %s.msh &" %name)
gmsh.write("%s.brep" % name)
gmsh.write("%s.msh" % name)
os.system("gmsh %s.brep &" % name)
os.system("gmsh %s.msh &" % name)
def test_PlaneSurface():
"""
......@@ -216,43 +259,66 @@ def test_PlaneSurface():
name = "test_PlaneSurface"
gmsh.model.add(name)
coords = [(0.,0.05), (0.05,0.), (1.8,0.), (2.0, 0.2), (2.0, 1.95), (1.95, 2.0), (0.2, 2.0), (0., 1.8)]
coords = [
(0.0, 0.05),
(0.05, 0.0),
(1.8, 0.0),
(2.0, 0.2),
(2.0, 1.95),
(1.95, 2.0),
(0.2, 2.0),
(0.0, 1.8),
]
pts = [geo.Point(np.array(c), 0.03) for c in coords]
lines = [geo.Line(pts[2*i-1], pts[2*i]) for i in range(len(pts)//2)]
centers = [geo.Point(np.array(c), 0.03) for c in [(0.05,0.05), (1.8,0.2), (1.95, 1.95), (0.2, 1.8)]]
arcs = [geo.Arc(pts[2*i], centers[i], pts[2*i+1]) for i in range(len(pts)//2)]
lines = [geo.Line(pts[2 * i - 1], pts[2 * i]) for i in range(len(pts) // 2)]
centers = [
geo.Point(np.array(c), 0.03)
for c in [(0.05, 0.05), (1.8, 0.2), (1.95, 1.95), (0.2, 1.8)]
]
arcs = [
geo.Arc(pts[2 * i], centers[i], pts[2 * i + 1]) for i in range(len(pts) // 2)
]
elmts_1D = [item for pair in zip(lines, arcs) for item in pair]
ll_1 = geo.LineLoop(elmts_1D, explicit=True)
coords = [(1.,1.), (3., 1.), (3., 3.), (1., 3.)]
coords = [(1.0, 1.0), (3.0, 1.0), (3.0, 3.0), (1.0, 3.0)]
vertc = [geo.Point(np.array(c), 0.01) for c in coords]
ll_2 = geo.LineLoop(vertc, explicit=False)
surf_1 = geo.PlaneSurface(ll_1)
surf_2 = geo.PlaneSurface(ll_2)
rect_vtcs = [geo.Point(np.array(c), 0.05) for c in [(4,2), (4,4), (6,4), (6,2)]]
hole_vtcs = [geo.Point(np.array(c), 0.02) for c in [(5-0.1,3), (5,3-0.5), (5+0.1,3), (5,3+0.5)]]
rect_vtcs = [geo.Point(np.array(c), 0.05) for c in [(4, 2), (4, 4), (6, 4), (6, 2)]]
hole_vtcs = [
geo.Point(np.array(c), 0.02)
for c in [(5 - 0.1, 3), (5, 3 - 0.5), (5 + 0.1, 3), (5, 3 + 0.5)]
]
rect_ll = geo.LineLoop(rect_vtcs, explicit=False)
hole_ll = geo.LineLoop(hole_vtcs, explicit=False)
surf_with_hole = geo.PlaneSurface(rect_ll, [hole_ll])
all_surf = [surf_1, surf_2, surf_with_hole]
print("Model : %s \n Add PlaneSurface instances to a gmsh model. \n Surfaces tag :"%name)
print(
"Model : %s \n Add PlaneSurface instances to a gmsh model. \n Surfaces tag :"
% name
)
for s in all_surf:
s.add_gmsh()
print(s.tag)
factory.synchronize()
data = gmsh.model.getEntities()
print("model name : %s"%name)
print("model name : %s" % name)
print(data)
print("Option Mesh.SaveAll is set to 1. Initial value : %i" %gmsh.option.getNumber('Mesh.SaveAll'))
gmsh.option.setNumber('Mesh.SaveAll',1)
print(
"Option Mesh.SaveAll is set to 1. Initial value : %i"
% gmsh.option.getNumber("Mesh.SaveAll")
)
gmsh.option.setNumber("Mesh.SaveAll", 1)
gmsh.model.mesh.generate(2)
gmsh.write("%s.brep"%name)
gmsh.write("%s.msh"%name)
os.system("gmsh %s.brep &" %name)
os.system("gmsh %s.msh &" %name)
gmsh.write("%s.brep" % name)
gmsh.write("%s.msh" % name)
os.system("gmsh %s.brep &" % name)
os.system("gmsh %s.msh &" % name)
def test_bool_ops():
......@@ -267,19 +333,39 @@ def test_bool_ops():
name = "test_PlaneSurface_bool_ops"
gmsh.model.add(name)
coords = [(0.,0.05), (0.05,0.), (1.8,0.), (2.0, 0.2), (2.0, 1.95), (1.95, 2.0), (0.2, 2.0), (0., 1.8)]
coords = [
(0.0, 0.05),
(0.05, 0.0),
(1.8, 0.0),
(2.0, 0.2),
(2.0, 1.95),
(1.95, 2.0),
(0.2, 2.0),
(0.0, 1.8),
]
pts = [geo.Point(np.array(c), 0.03) for c in coords]
lines = [geo.Line(pts[2*i-1], pts[2*i]) for i in range(len(pts)//2)]
centers = [geo.Point(np.array(c), 0.03) for c in [(0.05,0.05), (1.8,0.2), (1.95, 1.95), (0.2, 1.8)]]
arcs = [geo.Arc(pts[2*i], centers[i], pts[2*i+1]) for i in range(len(pts)//2)]
lines = [geo.Line(pts[2 * i - 1], pts[2 * i]) for i in range(len(pts) // 2)]
centers = [
geo.Point(np.array(c), 0.03)
for c in [(0.05, 0.05), (1.8, 0.2), (1.95, 1.95), (0.2, 1.8)]
]
arcs = [
geo.Arc(pts[2 * i], centers[i], pts[2 * i + 1]) for i in range(len(pts) // 2)
]
elmts_1D = [item for pair in zip(lines, arcs) for item in pair]
ll_1 = geo.LineLoop(elmts_1D, explicit=True)
coords = [(1.,1.), (3., 1.), (3., 3.), (1., 3.)]
coords = [(1.0, 1.0), (3.0, 1.0), (3.0, 3.0), (1.0, 3.0)]
vertc = [geo.Point(np.array(c), 0.1) for c in coords]
ll_2 = geo.LineLoop(vertc, explicit=False)
rect_vtcs = [geo.Point(np.array(c), 0.05) for c in [(4,2), (4,4), (6,4), (6,2)]]
hole_vtcs_1 = [geo.Point(np.array(c), 0.02) for c in [(5-0.1,3), (5,3-0.5), (5+0.1,3), (5,3+0.5)]]
hole_vtcs_2 = [geo.Point(np.array(c), 0.02) for c in [(5, 3-0.1), (5-0.5, 3), (5, 3+0.1), (5+0.5, 3)]]
rect_vtcs = [geo.Point(np.array(c), 0.05) for c in [(4, 2), (4, 4), (6, 4), (6, 2)]]