Advertisement
Guest User

Untitled

a guest
Feb 20th, 2018
68
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 8.05 KB | None | 0 0
  1. from PIL import Image, ImageDraw
  2. import numpy as np
  3. import random
  4.  
  5. default_color = (255, 0, 0)
  6.  
  7. default_size = (1000, 1000)
  8. default_boundary_z = 10
  9.  
  10. default_points_number = 500
  11.  
  12.  
  13. class Node:
  14.     def __init__(self, point, value):
  15.         self.x = point[0]
  16.         self.y = point[1]
  17.         self.value = value
  18.  
  19.     def __str__(self):
  20.         return "<" + str(self.x) + ", " + str(self.y) + ">"
  21.  
  22.     def __repr__(self):
  23.         return str(self)
  24.  
  25.  
  26. class Triangle:
  27.     def __init__(self, nodes):
  28.         self.nodes = nodes
  29.  
  30.     def __str__(self):
  31.         return str(self.nodes)
  32.  
  33.     def __repr__(self):
  34.         return str(self)
  35.  
  36.  
  37. def draw_line(image, xy1, xy2, fill=default_color):
  38.     draw = ImageDraw.Draw(image)
  39.     draw.line(xy1 + xy2, fill, width=1)
  40.     del draw
  41.  
  42.  
  43. def draw_value(image, point, value):
  44.     draw = ImageDraw.Draw(image)
  45.     draw.text(point, str(value), fill=(0, 0, 0, 1))
  46.     del draw
  47.  
  48.  
  49. def draw_interpolated(image, point, value):
  50.     draw = ImageDraw.Draw(image)
  51.     draw.ellipse((point[0] - 10, point[1] - 10, point[0] + 10, point[1] + 10), fill=default_color)
  52.     draw.text((point[0], point[1]), str(value), fill=(0, 0, 0, 1))
  53.     del draw
  54.  
  55. # ---------------------
  56. #  Private methods
  57. # ---------------------
  58.  
  59. def to_node(points):
  60.     return [Node(point[:-1], point[2]) for point in points]
  61.  
  62.  
  63. def pairs(array):
  64.     return zip(array[:-1] + [array[-1]], array[1:] + [array[0]])
  65.  
  66.  
  67. def point_in_triangle(point, triangle):
  68.     def sign(p1, p2, p3):
  69.         return (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y)
  70.     v1, v2, v3 = triangle.nodes
  71.     return sign(point, v1, v2) > 0.0 and sign(point, v2, v3) > 0.0 and sign(point, v3, v1) > 0.0
  72.  
  73.  
  74. # ---------------------
  75. # Triangulation methods
  76. # ---------------------
  77.  
  78.  
  79. def bounding_box(grid, image_size):
  80.     min_x, max_x = image_size[0], 0
  81.     min_y, max_y = image_size[1], 0
  82.     for point in grid:
  83.         if point[0] < min_x:
  84.             min_x = point[0]
  85.         if point[0] > max_x:
  86.             max_x = point[0]
  87.         if point[1] < min_y:
  88.             min_y = point[1]
  89.         if point[1] > max_y:
  90.             max_y = point[1]
  91.  
  92.     return [(min_x, min_y, default_boundary_z),
  93.             (max_x, min_y, default_boundary_z),
  94.             (max_x, max_y, default_boundary_z),
  95.             (min_x, max_y, default_boundary_z)]
  96.  
  97.  
  98. def in_circle_robust(node, triangle):
  99.     """Check if point p is inside of circle around
  100.     the triangle tri.
  101.    This is a robust predicate, slower than compare distance to centers
  102.    ref: http://www.cs.cmu.edu/~quake/robust.html
  103.    :param triangle: Triangle
  104.    :param node:  Node
  105.    """
  106.  
  107.     m1 = np.asarray([[n.x - node.x, n.y - node.y] for n in triangle.nodes])
  108.     m2 = np.sum(np.square(m1), axis=1).reshape((3, 1))
  109.     m = np.hstack((m1, m2))    # The 3x3 matrix to check
  110.     return np.linalg.det(m) > 0
  111.  
  112.  
  113. def polygon(connected_triangles):
  114.     if len(connected_triangles) == 1:
  115.         return connected_triangles[0].nodes
  116.  
  117.     def _polygon(triangles, polygon_nodes):
  118.         if len(triangles) == 0:
  119.             return polygon_nodes
  120.         if len(polygon_nodes) == 0:
  121.             triangle_first = triangles[0]
  122.             return _polygon(triangles[1:], polygon_nodes=triangle_first.nodes)
  123.  
  124.         def common_nodes(tri, nodes):
  125.             return [node for node in tri.nodes if node in nodes]
  126.  
  127.         triangle_candidate = next(tri for tri in triangles
  128.                                   if len(common_nodes(tri, polygon_nodes)) == 2)
  129.         triangles_except_candidate = [tri for tri in triangles
  130.                                       if tri != triangle_candidate]
  131.         point_new = [node for node in triangle_candidate.nodes
  132.                      if node not in polygon_nodes]
  133.         point_sep_1, point_sep_2 = [node for node in triangle_candidate.nodes
  134.                                     if node in polygon_nodes]
  135.         index_sep_1, index_sep_2 = polygon_nodes.index(point_sep_1),\
  136.                                    polygon_nodes.index(point_sep_2)
  137.         if abs(index_sep_1 - index_sep_2) == 1:
  138.             separation_index = min(index_sep_1, index_sep_2) + 1
  139.             polygon_nodes_new = polygon_nodes[:separation_index] +\
  140.                                 point_new + polygon_nodes[separation_index:]
  141.             return  _polygon(triangles_except_candidate, polygon_nodes=polygon_nodes_new)
  142.         else:
  143.             separation_index = 0
  144.             polygon_nodes_new = polygon_nodes[:separation_index] + point_new + \
  145.                                 polygon_nodes[separation_index:]
  146.             return  _polygon(triangles_except_candidate, polygon_nodes=polygon_nodes_new)
  147.  
  148.     return _polygon(connected_triangles, [])
  149.  
  150.  
  151. def build_destroy(triangles, nodes_to_insert):
  152.     """
  153.  
  154.    :param triangles: [Triangle]
  155.    :param nodes_to_insert: node to insert from grid
  156.    :return: triangulation result - [Triangle]
  157.    """
  158.     if len(nodes_to_insert) == 0:
  159.         return triangles
  160.  
  161.     node = nodes_to_insert[0]
  162.     triangles_to_destroy = [candidate for candidate in triangles
  163.                             if in_circle_robust(node, candidate)]
  164.     triangles_rest = [candidate for candidate in triangles
  165.                       if not in_circle_robust(node, candidate)]
  166.  
  167.     nodes_polygon = polygon(triangles_to_destroy)
  168.  
  169.     def _build_triangles_from_polygon(polygon_nodes, node_new):
  170.         assert len(polygon_nodes) >= 3
  171.         return [Triangle([node1, node2, node_new])
  172.                 for node1, node2 in pairs(polygon_nodes)]
  173.  
  174.     triangles_built = _build_triangles_from_polygon(polygon_nodes=nodes_polygon,
  175.                                                     node_new=node)
  176.  
  177.     return build_destroy(triangles_rest + triangles_built, nodes_to_insert[1:])
  178.  
  179.  
  180. def build_triangulation(grid, size):
  181.     box = bounding_box(grid=grid, image_size=size)
  182.     box_nodes = to_node(points=box)
  183.     grid_except_box = [point for point in grid if point not in box]
  184.  
  185.     triangle1 = Triangle(nodes=box_nodes[:-1])
  186.     triangle2 = Triangle(nodes=box_nodes[2:] + [box_nodes[0]])
  187.     triangles = build_destroy(triangles=[triangle1, triangle2],
  188.                               nodes_to_insert=to_node(grid_except_box))
  189.     return triangles
  190.  
  191.  
  192. # ---------------------
  193. # Interpolation
  194. # ---------------------
  195.  
  196.  
  197. def interpolate_in_triangle(point, triangle):
  198.     if not point_in_triangle(point, triangle):
  199.         assert False
  200.     v1, v2, v3 = triangle.nodes
  201.  
  202.     def distance(point1, point2):
  203.         return np.sqrt((point1.x - point2.x)**2 + (point2.y - point1.y)**2)
  204.  
  205.     d1, d2, d3 = [distance(v, point) for v in [v1, v2, v3]]
  206.     w1, w2, w3 = 1.0 / d1, 1.0 / d2, 1.0 / d3
  207.  
  208.     interpolated = (w1 * v1.value + w2*v2.value + w3 * v3.value) / (w1 + w2 + w3)
  209.     return interpolated
  210.  
  211.  
  212. def interpolate(point, triangulation):
  213.     containing_triangle = [tri for tri in triangulation if point_in_triangle(point, tri)][0]
  214.     return interpolate_in_triangle(point, containing_triangle)
  215.  
  216.  
  217. # ---------------------
  218. # Main
  219. # ---------------------
  220.  
  221.  
  222. def create_grid():
  223.     return [(
  224.                 random.randint(0, default_size[0]),
  225.                 random.randint(0, default_size[1]),
  226.                 random.randint(0, default_boundary_z)
  227.             ) for _ in range(default_points_number)]
  228.  
  229.  
  230. def main():
  231.     array = create_grid()
  232.     interpolating_point = array[-1]
  233.     interpolating_node = to_node([interpolating_point])[0]
  234.     grid = array[:-1]
  235.     triangles = build_triangulation(grid=grid, size=default_size)
  236.     result = interpolate(interpolating_node, triangulation=triangles)
  237.     print("Interpolated: " + str(result))
  238.  
  239.     image = Image.new("RGB", default_size, color='white')
  240.     for triangle in triangles:
  241.         for node1, node2 in pairs(triangle.nodes):
  242.             draw_line(image, (node1.x, node1.y), (node2.x, node2.y))
  243.  
  244.     for point in grid:
  245.         draw_value(image, point=(point[0], point[1]), value=point[2])
  246.  
  247.     draw_interpolated(image, interpolating_point, result)
  248.  
  249.     image.save("triangulation_result.png", "PNG")
  250.     return
  251.  
  252.  
  253. if __name__ == '__main__':
  254.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement