Finalize Polyline parsing but still arcs precision issue
This commit is contained in:
148
main.py
148
main.py
@@ -1,31 +1,33 @@
|
|||||||
|
|
||||||
from Enums import LINE_OVERLAP, LINE_THICKNESS_MODE, ROTATION
|
|
||||||
from PIL import Image, ImageDraw
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
from networks.geometry.Point3D import Point3D
|
|
||||||
from networks.geometry.Segment3D import Segment3D
|
|
||||||
from networks.geometry.Segment2D import Segment2D
|
|
||||||
from networks.geometry.Circle import Circle
|
|
||||||
from networks.geometry.Polyline import Polyline
|
|
||||||
from networks.geometry.Point2D import Point2D
|
|
||||||
import networks.roads.lines.Line as Line
|
|
||||||
import networks.roads.lanes.Lane as Lane
|
|
||||||
from gdpc import Editor, Block, geometry
|
|
||||||
import networks.geometry.curve_tools as curve_tools
|
|
||||||
import networks.geometry.Strip as Strip
|
|
||||||
import networks.geometry.segment_tools as segment_tools
|
|
||||||
import numpy as np
|
|
||||||
import json
|
import json
|
||||||
from buildings.Building import Building
|
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
import matplotlib
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
from gdpc import Block, Editor, geometry
|
||||||
|
from PIL import Image, ImageDraw
|
||||||
|
|
||||||
|
import networks.geometry.curve_tools as curve_tools
|
||||||
|
import networks.geometry.segment_tools as segment_tools
|
||||||
|
import networks.geometry.Strip as Strip
|
||||||
|
import networks.roads.lanes.Lane as Lane
|
||||||
|
import networks.roads.lines.Line as Line
|
||||||
|
from buildings.Building import Building
|
||||||
|
from Enums import LINE_OVERLAP, LINE_THICKNESS_MODE, ROTATION
|
||||||
|
from networks.geometry.Circle import Circle
|
||||||
|
from networks.geometry.Point2D import Point2D
|
||||||
|
from networks.geometry.Point3D import Point3D
|
||||||
|
from networks.geometry.point_tools import (
|
||||||
|
curved_corner_by_curvature,
|
||||||
|
curved_corner_by_distance,
|
||||||
|
)
|
||||||
|
from networks.geometry.Polyline import Polyline
|
||||||
|
from networks.geometry.Segment2D import Segment2D
|
||||||
|
from networks.geometry.Segment3D import Segment3D
|
||||||
from networks.roads import Road as Road
|
from networks.roads import Road as Road
|
||||||
from networks.roads.intersections import Intersection as Intersection
|
from networks.roads.intersections import Intersection as Intersection
|
||||||
|
|
||||||
from networks.geometry.point_tools import curved_corner_by_curvature, curved_corner_by_distance
|
|
||||||
|
|
||||||
|
|
||||||
import matplotlib
|
|
||||||
matplotlib.use('Agg')
|
matplotlib.use('Agg')
|
||||||
|
|
||||||
|
|
||||||
@@ -286,18 +288,26 @@ block_list = ["blue_concrete", "red_concrete", "green_concrete",
|
|||||||
# p = Polyline((Point2D(-1225, 468), Point2D(-1138, 481),
|
# p = Polyline((Point2D(-1225, 468), Point2D(-1138, 481),
|
||||||
# Point2D(-1188, 451), Point2D(-1176, 409), Point2D(-1179, 399)))
|
# Point2D(-1188, 451), Point2D(-1176, 409), Point2D(-1179, 399)))
|
||||||
|
|
||||||
w = 200
|
w = 100
|
||||||
|
|
||||||
n_points = 20
|
n_points = 8
|
||||||
min_val, max_val = -w, w
|
min_val, max_val = -w, w
|
||||||
|
|
||||||
random_points = [Point2D(random.randint(min_val, max_val), random.randint(
|
random_points = [Point2D(random.randint(min_val, max_val), random.randint(
|
||||||
min_val, max_val)) for _ in range(n_points)]
|
min_val, max_val)) for _ in range(n_points)]
|
||||||
|
|
||||||
|
print(random_points)
|
||||||
|
print("\n\n")
|
||||||
|
|
||||||
|
|
||||||
# random_points = (Point2D(-75, -75), Point2D(0, -75), Point2D(75, 75),
|
# random_points = (Point2D(-75, -75), Point2D(0, -75), Point2D(75, 75),
|
||||||
# Point2D(75, -50), Point2D(-50, 50), Point2D(0, 0))
|
# Point2D(75, -50), Point2D(-50, 50), Point2D(0, 0))
|
||||||
|
|
||||||
|
# random_points = random_points[0].optimized_path(random_points)
|
||||||
|
|
||||||
|
# random_points = [Point2D(-40, -56), Point2D(-94, 92), Point2D(19, -47), Point2D(
|
||||||
|
# 100, 59), Point2D(-85, -73), Point2D(-33, -9), Point2D(57, -25), Point2D(51, -34)]
|
||||||
|
|
||||||
random_points = random_points[0].optimized_path(random_points)
|
random_points = random_points[0].optimized_path(random_points)
|
||||||
|
|
||||||
p = Polyline(random_points)
|
p = Polyline(random_points)
|
||||||
@@ -316,10 +326,8 @@ image = Image.new('RGB', (width, height), 'black')
|
|||||||
|
|
||||||
draw = ImageDraw.Draw(image)
|
draw = ImageDraw.Draw(image)
|
||||||
|
|
||||||
print(p.output_points)
|
|
||||||
|
|
||||||
for i in range(len(p.output_points)-1):
|
for i in range(len(p.output_points)-1):
|
||||||
if p.output_points[i] != None:
|
if p.output_points[i] != 0:
|
||||||
s = Segment2D(Point2D(p.output_points[i].x, p.output_points[i].y), Point2D(
|
s = Segment2D(Point2D(p.output_points[i].x, p.output_points[i].y), Point2D(
|
||||||
p.output_points[i+1].x, p.output_points[i+1].y))
|
p.output_points[i+1].x, p.output_points[i+1].y))
|
||||||
s.segment_thick(ww, LINE_THICKNESS_MODE.MIDDLE)
|
s.segment_thick(ww, LINE_THICKNESS_MODE.MIDDLE)
|
||||||
@@ -331,40 +339,40 @@ for i in range(len(p.output_points)-1):
|
|||||||
w-s.points_thick[j].y), fill='grey')
|
w-s.points_thick[j].y), fill='grey')
|
||||||
|
|
||||||
|
|
||||||
for i in range(2, len(p.get_arcs_intersections())-2):
|
# for i in range(2, len(p.get_arcs_intersections())-2):
|
||||||
|
|
||||||
s = Segment2D(Point2D(p.acrs_intersections[i][0].x, p.acrs_intersections[i][0].y), Point2D(
|
# s = Segment2D(Point2D(p.acrs_intersections[i][0].x, p.acrs_intersections[i][0].y), Point2D(
|
||||||
p.acrs_intersections[i-1][-1].x, p.acrs_intersections[i-1][-1].y))
|
# p.acrs_intersections[i-1][-1].x, p.acrs_intersections[i-1][-1].y))
|
||||||
s.segment_thick(ww, LINE_THICKNESS_MODE.MIDDLE)
|
# s.segment_thick(ww, LINE_THICKNESS_MODE.MIDDLE)
|
||||||
|
|
||||||
for j in range(len(s.points_thick)-1):
|
# for j in range(len(s.points_thick)-1):
|
||||||
# editor.placeBlock(
|
# # editor.placeBlock(
|
||||||
# s.coordinates[j].coordinate, Block("cyan_concrete"))
|
# # s.coordinates[j].coordinate, Block("cyan_concrete"))
|
||||||
draw.point((s.points_thick[j].x+w,
|
# draw.point((s.points_thick[j].x+w,
|
||||||
w-s.points_thick[j].y), fill='white')
|
# w-s.points_thick[j].y), fill='green')
|
||||||
draw.point((p.acrs_intersections[i][0].x+w,
|
# draw.point((p.acrs_intersections[i][0].x+w,
|
||||||
w-p.acrs_intersections[i][0].y), fill='blue')
|
# w-p.acrs_intersections[i][0].y), fill='green')
|
||||||
draw.point((p.acrs_intersections[i][-1].x+w,
|
# draw.point((p.acrs_intersections[i][-1].x+w,
|
||||||
w-p.acrs_intersections[i][-1].y), fill='red')
|
# w-p.acrs_intersections[i][-1].y), fill='green')
|
||||||
|
|
||||||
|
|
||||||
for i in range(len(center)):
|
# for i in range(len(center)):
|
||||||
if center[i]:
|
# if center[i]:
|
||||||
circle = Circle(center[i])
|
# circle = Circle(center[i])
|
||||||
circle.circle_thick(round(radius[i]-ww/2), round(radius[i]+ww/2))
|
# circle.circle_thick(round(radius[i]-ww/2), round(radius[i]+ww/2))
|
||||||
for j in range(len(circle.points_thick)-1):
|
# for j in range(len(circle.points_thick)-1):
|
||||||
if circle.points_thick[j].is_in_triangle(p.acrs_intersections[i][0], p.acrs_intersections[i][1], p.acrs_intersections[i][2]):
|
# if circle.points_thick[j].is_in_triangle(p.acrs_intersections[i][0], p.acrs_intersections[i][1], p.acrs_intersections[i][2]):
|
||||||
# editor.placeBlock(
|
# # editor.placeBlock(
|
||||||
# (circle.coordinates[j].x, y, circle.coordinates[j].y), Block("white_concrete"))
|
# # (circle.coordinates[j].x, y, circle.coordinates[j].y), Block("white_concrete"))
|
||||||
draw.point((circle.points_thick[j].x+w,
|
# draw.point((circle.points_thick[j].x+w,
|
||||||
w-circle.points_thick[j].y), fill='white')
|
# w-circle.points_thick[j].y), fill='green')
|
||||||
circle.circle(radius[i])
|
# circle.circle(radius[i])
|
||||||
for j in range(len(circle.points)-1):
|
# for j in range(len(circle.points)-1):
|
||||||
if circle.points[j].is_in_triangle(p.acrs_intersections[i][0], p.acrs_intersections[i][1], p.acrs_intersections[i][2]):
|
# if circle.points[j].is_in_triangle(p.acrs_intersections[i][0], p.acrs_intersections[i][1], p.acrs_intersections[i][2]):
|
||||||
# editor.placeBlock(
|
# # editor.placeBlock(
|
||||||
# (circle.coordinates[j].x, y, circle.coordinates[j].y), Block("white_concrete"))
|
# # (circle.coordinates[j].x, y, circle.coordinates[j].y), Block("white_concrete"))
|
||||||
draw.point((circle.points[j].x+w,
|
# draw.point(
|
||||||
w-circle.points[j].y), fill='purple')
|
# (circle.points[j].x+w, w-circle.points[j].y), fill='green')
|
||||||
|
|
||||||
s1 = Segment2D(Point2D(p.acrs_intersections[1][0].x, p.acrs_intersections[1][0].y), Point2D(
|
s1 = Segment2D(Point2D(p.acrs_intersections[1][0].x, p.acrs_intersections[1][0].y), Point2D(
|
||||||
p.output_points[0].x, p.output_points[0].y))
|
p.output_points[0].x, p.output_points[0].y))
|
||||||
@@ -372,7 +380,7 @@ s1.segment_thick(ww, LINE_THICKNESS_MODE.MIDDLE)
|
|||||||
|
|
||||||
for j in range(len(s1.points_thick)-1):
|
for j in range(len(s1.points_thick)-1):
|
||||||
draw.point((s1.points_thick[j].x+w,
|
draw.point((s1.points_thick[j].x+w,
|
||||||
w-s1.points_thick[j].y), fill='white')
|
w-s1.points_thick[j].y), fill='grey')
|
||||||
|
|
||||||
s1 = Segment2D(Point2D(p.acrs_intersections[-2][2].x, p.acrs_intersections[-2][2].y), Point2D(
|
s1 = Segment2D(Point2D(p.acrs_intersections[-2][2].x, p.acrs_intersections[-2][2].y), Point2D(
|
||||||
p.output_points[-1].x, p.output_points[-1].y))
|
p.output_points[-1].x, p.output_points[-1].y))
|
||||||
@@ -380,10 +388,34 @@ s1.segment_thick(ww, LINE_THICKNESS_MODE.MIDDLE)
|
|||||||
|
|
||||||
for j in range(len(s1.points_thick)-1):
|
for j in range(len(s1.points_thick)-1):
|
||||||
draw.point((s1.points_thick[j].x+w,
|
draw.point((s1.points_thick[j].x+w,
|
||||||
w-s1.points_thick[j].y), fill='white')
|
w-s1.points_thick[j].y), fill='grey')
|
||||||
|
|
||||||
|
for i in range(0, len(p.arcs)):
|
||||||
|
for j in range(len(p.arcs[i])):
|
||||||
|
draw.point((p.arcs[i][j].x+w, w-p.arcs[i][j].y), fill='white')
|
||||||
|
|
||||||
|
|
||||||
|
for i in range(1, len(p.segments)-1):
|
||||||
|
for j in range(len(p.segments[i].segment())):
|
||||||
|
draw.point((p.segments[i].points[j].x+w,
|
||||||
|
w-p.segments[i].points[j].y), fill='white')
|
||||||
|
|
||||||
|
for i in range(1, len(p.centers)-1):
|
||||||
|
draw.point((p.centers[i].x+w, w-p.centers[i].y), fill='red')
|
||||||
|
draw.point((p.acrs_intersections[i][0].x+w,
|
||||||
|
w-p.acrs_intersections[i][0].y), fill='blue')
|
||||||
|
draw.point((p.acrs_intersections[i][1].x+w,
|
||||||
|
w-p.acrs_intersections[i][1].y), fill='purple')
|
||||||
|
draw.point((p.acrs_intersections[i][2].x+w,
|
||||||
|
w-p.acrs_intersections[i][2].y), fill='blue')
|
||||||
|
|
||||||
|
|
||||||
image.save('output_image.png')
|
image.save('output_image.png')
|
||||||
|
|
||||||
|
# s = Segment2D(Point2D(-88, -12), Point2D(9, 75))
|
||||||
|
# s.segment_thick(3, LINE_THICKNESS_MODE.MIDDLE)
|
||||||
|
# print(s.points)
|
||||||
|
|
||||||
# s = Segment2D(Point2D(0, 0), Point2D(10, 10)).perpendicular(10)
|
# s = Segment2D(Point2D(0, 0), Point2D(10, 10)).perpendicular(10)
|
||||||
# print(s)
|
# print(s)
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,24 @@
|
|||||||
from networks.geometry.Point2D import Point2D
|
from math import cos, pi, sin
|
||||||
from math import cos, sin, pi
|
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from networks.geometry.Point2D import Point2D
|
||||||
|
|
||||||
|
|
||||||
class Circle:
|
class Circle:
|
||||||
def __init__(self, center: Point2D):
|
def __init__(self, center: Point2D):
|
||||||
self.center = center
|
self.center = center
|
||||||
|
|
||||||
self.radius = None
|
self.radius = None
|
||||||
self.points = []
|
self.points: List[Point2D] = []
|
||||||
|
|
||||||
self.inner = None
|
self.inner = None
|
||||||
self.outer = None
|
self.outer = None
|
||||||
self.points_thick = []
|
self.points_thick: List[Point2D] = []
|
||||||
|
|
||||||
self.spaced_radius = None
|
self.spaced_radius = None
|
||||||
self.spaced_points = []
|
self.spaced_points: List[Point2D] = []
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"Circle(center: {self.center}, radius: {self.radius}, spaced_radius: {self.spaced_radius}, inner: {self.inner}, outer: {self.outer})"
|
return f"Circle(center: {self.center}, radius: {self.radius}, spaced_radius: {self.spaced_radius}, inner: {self.inner}, outer: {self.outer})"
|
||||||
@@ -43,6 +46,7 @@ class Circle:
|
|||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
return self.points
|
||||||
|
|
||||||
def circle_thick(self, inner: int, outer: int) -> List[Point2D]:
|
def circle_thick(self, inner: int, outer: int) -> List[Point2D]:
|
||||||
"""Compute discrete value of a 2d-circle with thickness.
|
"""Compute discrete value of a 2d-circle with thickness.
|
||||||
@@ -114,16 +118,17 @@ class Circle:
|
|||||||
center = self.center
|
center = self.center
|
||||||
|
|
||||||
self.spaced_points = [
|
self.spaced_points = [
|
||||||
Point2D(cos(2 * pi / number * i) * radius,
|
Point2D(round(cos(2 * pi / number * i) * radius),
|
||||||
sin(2 * pi / number * i) * radius)
|
round(sin(2 * pi / number * i) * radius))
|
||||||
for i in range(0, number + 1)
|
for i in range(0, number + 1)
|
||||||
]
|
]
|
||||||
|
|
||||||
for i in range(len(self.spaced_points)):
|
for i in range(len(self.spaced_points)):
|
||||||
self.spaced_points[i] = Point2D(
|
current_point = Point2D(
|
||||||
self.spaced_points[i].x + center.x,
|
self.spaced_points[i].x + center.x,
|
||||||
self.spaced_points[i].y + center.y
|
self.spaced_points[i].y + center.y
|
||||||
).round()
|
).round()
|
||||||
|
self.spaced_points[i] = current_point
|
||||||
return self.spaced_points
|
return self.spaced_points
|
||||||
|
|
||||||
def _x_line(self, x1, x2, y):
|
def _x_line(self, x1, x2, y):
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import numpy as np
|
|
||||||
from typing import List
|
|
||||||
from math import atan2, sqrt
|
from math import atan2, sqrt
|
||||||
|
from typing import List, Union
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
from Enums import ROTATION
|
from Enums import ROTATION
|
||||||
|
|
||||||
|
|
||||||
@@ -21,10 +23,26 @@ class Point2D:
|
|||||||
return self.x == other.x and self.y == other.y
|
return self.x == other.x and self.y == other.y
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def __add__(self, other):
|
||||||
|
if isinstance(other, np.ndarray) and other.shape == (2,):
|
||||||
|
return Point2D(self.x + other[0], self.y + other[1])
|
||||||
|
elif isinstance(other, Point2D):
|
||||||
|
return Point2D(self.x + other.x, self.y + other.y)
|
||||||
|
else:
|
||||||
|
raise TypeError(f"Unsupported type for addition: {type(other)}")
|
||||||
|
|
||||||
|
def __sub__(self, other):
|
||||||
|
if isinstance(other, np.ndarray) and other.shape == (2,):
|
||||||
|
return Point2D(self.x - other[0], self.y - other[1])
|
||||||
|
elif isinstance(other, Point2D):
|
||||||
|
return Point2D(self.x - other.x, self.y - other.y)
|
||||||
|
else:
|
||||||
|
raise TypeError(f"Unsupported type for subtraction: {type(other)}")
|
||||||
|
|
||||||
def is_in_triangle(self, xy0: "Point2D", xy1: "Point2D", xy2: "Point2D"):
|
def is_in_triangle(self, xy0: "Point2D", xy1: "Point2D", xy2: "Point2D"):
|
||||||
"""Returns True is the point is in a triangle defined by 3 others points.
|
"""Returns True is the point is in a triangle defined by 3 others points.
|
||||||
|
|
||||||
From: https://stackoverflow.com/questions/2049582/how-to-determine-if-a-point-is-in-a-2d-triangle#:~:text=A%20simple%20way%20is%20to,point%20is%20inside%20the%20triangle.
|
From: https://stackoverflow.com/questions/2049582/how-to-determine-if-a-point-is-in-a-2d-triangle
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
xy0 (Type[Point2D]): Point of the triangle.
|
xy0 (Type[Point2D]): Point of the triangle.
|
||||||
@@ -190,12 +208,21 @@ class Point2D:
|
|||||||
return abs(x1 * y2 - x2 * y1) < 1e-12
|
return abs(x1 * y2 - x2 * y1) < 1e-12
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def to_vectors(points: List["Point3D"]) -> List[np.array]:
|
def to_arrays(points: Union[List["Point2D"], "Point2D"]) -> Union[List[np.array], "Point2D"]:
|
||||||
vectors = []
|
if isinstance(points, list):
|
||||||
for point in points:
|
vectors = []
|
||||||
vectors.append(np.array(point.coordinates))
|
for point in points:
|
||||||
|
vectors.append(np.array(point.coordinates))
|
||||||
if (len(vectors) == 1):
|
|
||||||
return vectors[0]
|
|
||||||
else:
|
|
||||||
return vectors
|
return vectors
|
||||||
|
else:
|
||||||
|
return np.array(points.coordinates)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_arrays(vectors: Union[List[np.array], "Point2D"]) -> Union[List["Point2D"], "Point2D"]:
|
||||||
|
if isinstance(vectors, list):
|
||||||
|
points = []
|
||||||
|
for vector in vectors:
|
||||||
|
points.append(Point2D(vector[0], vector[1]))
|
||||||
|
return points
|
||||||
|
else:
|
||||||
|
return Point2D(vectors[0], vectors[1])
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
from math import sqrt
|
||||||
from typing import List
|
from typing import List
|
||||||
from math import atan2, sqrt
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
@@ -80,3 +81,14 @@ class Point3D:
|
|||||||
return vectors[0]
|
return vectors[0]
|
||||||
else:
|
else:
|
||||||
return vectors
|
return vectors
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_arrays(vectors: List[np.array]) -> List["Point3D"]:
|
||||||
|
points = []
|
||||||
|
for vector in vectors:
|
||||||
|
points.append(Point3D(vector[0], vector[1], vector[2]))
|
||||||
|
|
||||||
|
if (len(points) == 1):
|
||||||
|
return points[0]
|
||||||
|
else:
|
||||||
|
return points
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
from networks.geometry.Point2D import Point2D
|
from math import inf, sqrt
|
||||||
|
from typing import List, Tuple, Union
|
||||||
|
|
||||||
from math import sqrt, inf
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
from networks.geometry.Circle import Circle
|
||||||
|
from networks.geometry.Point2D import Point2D
|
||||||
|
from networks.geometry.Segment2D import Segment2D
|
||||||
|
|
||||||
|
# from Enums import LINE_THICKNESS_MODE, LINE_OVERLAP
|
||||||
|
|
||||||
|
|
||||||
class Polyline:
|
class Polyline:
|
||||||
def __init__(self, points: list["Point2D"]):
|
def __init__(self, points: List[Point2D]):
|
||||||
"""A polyline with smooth corners, only composed of segments and circle arc.
|
"""A polyline with smooth corners, only composed of segments and circle arc.
|
||||||
|
|
||||||
Mathematics and algorithms behind this can be found here: https://cdr.lib.unc.edu/concern/dissertations/pz50gw814?locale=en, E2 Construction of arc roads from polylines, page 210.
|
Mathematics and algorithms behind this can be found here: https://cdr.lib.unc.edu/concern/dissertations/pz50gw814?locale=en, E2 Construction of arc roads from polylines, page 210.
|
||||||
@@ -18,7 +24,8 @@ class Polyline:
|
|||||||
|
|
||||||
>>> Polyline((Point2D(0, 0), Point2D(0, 10), Point2D(50, 10), Point2D(20, 20)))
|
>>> Polyline((Point2D(0, 0), Point2D(0, 10), Point2D(50, 10), Point2D(20, 20)))
|
||||||
"""
|
"""
|
||||||
self.points_array = Point2D.to_vectors(
|
self.output_points = points
|
||||||
|
self.points_array = Point2D.to_arrays(
|
||||||
self._remove_collinear_points(points))
|
self._remove_collinear_points(points))
|
||||||
self.length_polyline = len(self.points_array)
|
self.length_polyline = len(self.points_array)
|
||||||
|
|
||||||
@@ -26,36 +33,46 @@ class Polyline:
|
|||||||
raise ValueError("The list must contain at least 4 elements.")
|
raise ValueError("The list must contain at least 4 elements.")
|
||||||
|
|
||||||
self.vectors = [None] * self.length_polyline # v
|
self.vectors = [None] * self.length_polyline # v
|
||||||
self.lengths = [None] * (self.length_polyline - 1) # l
|
self.lengths = [0] * (self.length_polyline - 1) # l
|
||||||
self.unit_vectors = [None] * self.length_polyline # n
|
self.unit_vectors = [None] * self.length_polyline # n
|
||||||
self.tangente = [0] * self.length_polyline # f
|
self.tangente = [0] * self.length_polyline # f
|
||||||
|
|
||||||
# alpha, maximum radius factor
|
# alpha, maximum radius factor
|
||||||
self.alpha_radii = [None] * self.length_polyline
|
self.alpha_radii = [0] * self.length_polyline
|
||||||
|
|
||||||
self.radii = [None] * self.length_polyline # r
|
# Useful outputs. In order to not break indexation, each list has the same length, even if for n points, there is n-2 radius.
|
||||||
self.centers = [None] * self.length_polyline # c
|
# Lists will start and end with None.
|
||||||
|
self.radii = [0] * self.length_polyline # r, list of points
|
||||||
|
self.centers = [None] * self.length_polyline # c, list of points
|
||||||
|
# list of tuple of points (first intersection, corresponding corner, last intersection)
|
||||||
self.acrs_intersections = [None] * self.length_polyline
|
self.acrs_intersections = [None] * self.length_polyline
|
||||||
|
self.arcs = [[]] * self.length_polyline # list of points
|
||||||
|
# self.not_arcs = [[]] * self.length_polyline
|
||||||
|
|
||||||
|
# For n points, there is n-1 segments. Last element should stays None.
|
||||||
|
self.segments = [None] * \
|
||||||
|
self.length_polyline # list of segments
|
||||||
|
|
||||||
|
# Run procedure
|
||||||
self._compute_requirements()
|
self._compute_requirements()
|
||||||
self._compute_alpha_radii()
|
self._compute_alpha_radii()
|
||||||
|
|
||||||
self._alpha_assign(0, self.length_polyline-1)
|
self._alpha_assign(0, self.length_polyline-1)
|
||||||
|
self.get_radii()
|
||||||
self.output_points = points
|
self.get_centers()
|
||||||
|
self.get_arcs_intersections()
|
||||||
|
self.get_arcs()
|
||||||
|
self.get_segments()
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return str(self.alpha_radii)
|
return str(self.alpha_radii)
|
||||||
|
|
||||||
def get_radii(self):
|
def get_radii(self) -> List[Union[int]]:
|
||||||
for i in range(1, self.length_polyline-1):
|
for i in range(1, self.length_polyline-1):
|
||||||
self.radii[i] = round(self.alpha_radii[i] * self.tangente[i])
|
self.radii[i] = round(self.alpha_radii[i] * self.tangente[i])
|
||||||
return self.radii
|
return self.radii
|
||||||
|
|
||||||
def get_centers(self):
|
def get_centers(self) -> List[Union[Point2D, None]]:
|
||||||
if self.radii == [None] * self.length_polyline:
|
|
||||||
raise ValueError("No radii found. Run get_radii before.")
|
|
||||||
|
|
||||||
for i in range(1, self.length_polyline-1):
|
for i in range(1, self.length_polyline-1):
|
||||||
bisector = (self.unit_vectors[i] - self.unit_vectors[i-1]) / (
|
bisector = (self.unit_vectors[i] - self.unit_vectors[i-1]) / (
|
||||||
np.linalg.norm(self.unit_vectors[i] - self.unit_vectors[i-1]))
|
np.linalg.norm(self.unit_vectors[i] - self.unit_vectors[i-1]))
|
||||||
@@ -65,16 +82,68 @@ class Polyline:
|
|||||||
self.centers[i] = Point2D(array[0], array[1]).round()
|
self.centers[i] = Point2D(array[0], array[1]).round()
|
||||||
return self.centers
|
return self.centers
|
||||||
|
|
||||||
def get_arcs_intersections(self):
|
def get_arcs_intersections(self) -> List[Tuple[Point2D]]:
|
||||||
|
"""Get arcs intersections points.
|
||||||
|
|
||||||
|
First and last elements elements of the list should be None. For n points, there are n-1 segments, and n-2 angle.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[tuple(Point2D)]: List of tuples composed - in order - of the first arc points, the corner points, the last arc points. The corresponding arc circle is inside this triangle.
|
||||||
|
"""
|
||||||
for i in range(1, self.length_polyline-1):
|
for i in range(1, self.length_polyline-1):
|
||||||
point_1 = self.points_array[i] - \
|
point_1 = Point2D.from_arrays(self.points_array[i] -
|
||||||
self.alpha_radii[i] * self.unit_vectors[i-1]
|
self.alpha_radii[i] * self.unit_vectors[i-1])
|
||||||
point_2 = self.points_array[i] + \
|
point_2 = Point2D.from_arrays(self.points_array[i] +
|
||||||
self.alpha_radii[i] * self.unit_vectors[i]
|
self.alpha_radii[i] * self.unit_vectors[i])
|
||||||
self.acrs_intersections[i] = Point2D(
|
self.acrs_intersections[i] = point_1.round(), Point2D.from_arrays(
|
||||||
point_1[0], point_1[1]).round(), Point2D(self.points_array[i][0], self.points_array[i][1]), Point2D(point_2[0], point_2[1]).round()
|
self.points_array[i]), point_2.round()
|
||||||
return self.acrs_intersections
|
return self.acrs_intersections
|
||||||
|
|
||||||
|
def get_arcs(self) -> List[Point2D]:
|
||||||
|
for i in range(1, self.length_polyline-1):
|
||||||
|
circle = Circle(self.centers[i])
|
||||||
|
circle.circle(self.radii[i])
|
||||||
|
for j in range(len(circle.points)):
|
||||||
|
if circle.points[j].is_in_triangle(self.acrs_intersections[i][0], self.acrs_intersections[i][1], self.acrs_intersections[i][2]):
|
||||||
|
self.arcs[i].append(circle.points[j])
|
||||||
|
# for j in range(len(circle.points)):
|
||||||
|
# if (circle.points[j] in Segment2D(self.acrs_intersections[i][0], self.acrs_intersections[i][1]).segment(LINE_OVERLAP.MINOR)):
|
||||||
|
# self.not_arcs[i].append(circle.points[j])
|
||||||
|
# print("hzeh")
|
||||||
|
# if (circle.points[j] in Segment2D(self.acrs_intersections[i][1], self.acrs_intersections[i][2]).segment(LINE_OVERLAP.MINOR)):
|
||||||
|
# self.not_arcs[i].append(circle.points[j])
|
||||||
|
# print("hzeh")
|
||||||
|
# if (circle.points[j] in Segment2D(self.acrs_intersections[i][2], self.acrs_intersections[i][0]).segment(LINE_OVERLAP.MINOR)):
|
||||||
|
# self.not_arcs[i].append(circle.points[j])
|
||||||
|
# print("hzeh")
|
||||||
|
return self.arcs
|
||||||
|
|
||||||
|
def get_segments(self) -> List[Segment2D]:
|
||||||
|
"""Get the segments between the circle arcs and at the start and end.
|
||||||
|
|
||||||
|
Last list element should be None, and last usable index is -2 or self.length_polyline - 2. For n points, there are n-1 segments.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[Segment2D]: List of segments in order.
|
||||||
|
"""
|
||||||
|
# Get first segment.
|
||||||
|
# segments index is 0, corresponding to the first points_array to the first point ([0]) of the first arc (acrs_intersections[1]).
|
||||||
|
# First arc index is 1 because index 0 is None due to fix list lenght. Is it a good choice?
|
||||||
|
self.segments[1] = Segment2D(Point2D.from_arrays(
|
||||||
|
self.points_array[0]), self.acrs_intersections[1][0])
|
||||||
|
|
||||||
|
# Get segments between arcs
|
||||||
|
for i in range(2, self.length_polyline - 2):
|
||||||
|
self.segments[i] = Segment2D(Point2D(self.acrs_intersections[i][0].x, self.acrs_intersections[i][0].y), Point2D(
|
||||||
|
self.acrs_intersections[i-1][-1].x, self.acrs_intersections[i-1][-1].y))
|
||||||
|
|
||||||
|
# Get last segment. Index is -2 because last index -1 should be None due to the same list lenght.
|
||||||
|
# For n points, there are n-1 segments.
|
||||||
|
self.segments[-2] = Segment2D(Point2D.from_arrays(
|
||||||
|
self.points_array[-1]), self.acrs_intersections[-2][2])
|
||||||
|
|
||||||
|
return self.segments
|
||||||
|
|
||||||
def _alpha_assign(self, start_index: int, end_index: int):
|
def _alpha_assign(self, start_index: int, end_index: int):
|
||||||
"""
|
"""
|
||||||
The alpha-assign procedure assigning radii based on a polyline.
|
The alpha-assign procedure assigning radii based on a polyline.
|
||||||
@@ -101,8 +170,8 @@ class Polyline:
|
|||||||
minimum_radius, minimum_index = current_radius, i
|
minimum_radius, minimum_index = current_radius, i
|
||||||
alpha_low, alpha_high = alpha_a, alpha_b
|
alpha_low, alpha_high = alpha_a, alpha_b
|
||||||
|
|
||||||
alpha_a = min(self.lengths[end_index-2],
|
alpha_a = min(
|
||||||
self.lengths[end_index-1]-self.alpha_radii[end_index])
|
self.lengths[end_index-2], self.lengths[end_index-1]-self.alpha_radii[end_index])
|
||||||
|
|
||||||
current_radius = max(self.tangente[end_index-1]*alpha_a, self.tangente[end_index]
|
current_radius = max(self.tangente[end_index-1]*alpha_a, self.tangente[end_index]
|
||||||
* self.alpha_radii[end_index]) # Radius at final segment
|
* self.alpha_radii[end_index]) # Radius at final segment
|
||||||
@@ -123,9 +192,8 @@ class Polyline:
|
|||||||
"""
|
"""
|
||||||
Returns the radius that balances the radii on either end segement i.
|
Returns the radius that balances the radii on either end segement i.
|
||||||
"""
|
"""
|
||||||
|
alpha_a = min(self.lengths[i-1], (self.lengths[i] *
|
||||||
alpha_a = min(self.lengths[i-1], (self.lengths[i]*self.tangente[i+1]) /
|
self.tangente[i+1])/(self.tangente[i] + self.tangente[i+1]))
|
||||||
(self.tangente[i] + self.tangente[i+1]))
|
|
||||||
alpha_b = min(self.lengths[i+1], self.lengths[i]-alpha_a)
|
alpha_b = min(self.lengths[i+1], self.lengths[i]-alpha_a)
|
||||||
return alpha_a, alpha_b, min(self.tangente[i]*alpha_a, self.tangente[i+1]*alpha_b)
|
return alpha_a, alpha_b, min(self.tangente[i]*alpha_a, self.tangente[i+1]*alpha_b)
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,23 @@
|
|||||||
from typing import List
|
from typing import List, Union
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
from Enums import LINE_OVERLAP, LINE_THICKNESS_MODE
|
from Enums import LINE_OVERLAP, LINE_THICKNESS_MODE
|
||||||
from networks.geometry.Point2D import Point2D
|
from networks.geometry.Point2D import Point2D
|
||||||
from math import sqrt
|
|
||||||
|
|
||||||
|
|
||||||
class Segment2D:
|
class Segment2D:
|
||||||
def __init__(self, start: Point2D, end: Point2D):
|
def __init__(self, start: Point2D, end: Point2D):
|
||||||
self.start = start
|
self.start = start
|
||||||
self.end = end
|
self.end = end
|
||||||
self.points = []
|
self.points: List[Point2D] = []
|
||||||
self.points_thick = []
|
self.points_thick: List[Point2D] = []
|
||||||
self.thickness = None
|
self.thickness = None
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return str(self.points)
|
return str(f"Segment2D(start: {self.start}, end: {self.end}, points: {self.points})")
|
||||||
|
|
||||||
def segment(self, start: Point2D = None, end: Point2D = None, overlap: LINE_OVERLAP = LINE_OVERLAP.NONE, _is_computing_thickness: bool = False) -> List[Point2D]:
|
def segment(self, start: Point2D = None, end: Point2D = None, overlap: LINE_OVERLAP = LINE_OVERLAP.NONE, _is_computing_thickness: bool = False) -> Union[List[Point2D], None]:
|
||||||
"""Modified Bresenham draw (line) with optional overlap.
|
"""Modified Bresenham draw (line) with optional overlap.
|
||||||
|
|
||||||
From: https://github.com/ArminJo/Arduino-BlueDisplay/blob/master/src/LocalGUI/ThickLine.hpp
|
From: https://github.com/ArminJo/Arduino-BlueDisplay/blob/master/src/LocalGUI/ThickLine.hpp
|
||||||
@@ -29,7 +31,7 @@ class Segment2D:
|
|||||||
>>> Segment2D(Point2D(0, 0), Point2D(10, 15))
|
>>> Segment2D(Point2D(0, 0), Point2D(10, 15))
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if start == None or end == None:
|
if start is None or end is None:
|
||||||
start = self.start.copy()
|
start = self.start.copy()
|
||||||
end = self.end.copy()
|
end = self.end.copy()
|
||||||
else:
|
else:
|
||||||
@@ -90,6 +92,7 @@ class Segment2D:
|
|||||||
|
|
||||||
if not _is_computing_thickness:
|
if not _is_computing_thickness:
|
||||||
return self.points
|
return self.points
|
||||||
|
return None
|
||||||
|
|
||||||
def segment_thick(self, thickness: int, thickness_mode: LINE_THICKNESS_MODE) -> List[Point2D]:
|
def segment_thick(self, thickness: int, thickness_mode: LINE_THICKNESS_MODE) -> List[Point2D]:
|
||||||
"""Bresenham with thickness.
|
"""Bresenham with thickness.
|
||||||
@@ -208,7 +211,7 @@ class Segment2D:
|
|||||||
self.segment(
|
self.segment(
|
||||||
start, end, overlap=overlap, _is_computing_thickness=True)
|
start, end, overlap=overlap, _is_computing_thickness=True)
|
||||||
|
|
||||||
return self.points
|
return self.points_thick
|
||||||
|
|
||||||
def perpendicular(self, distance: int) -> List[Point2D]:
|
def perpendicular(self, distance: int) -> List[Point2D]:
|
||||||
"""Compute perpendicular points from both side of the segment placed at start level.
|
"""Compute perpendicular points from both side of the segment placed at start level.
|
||||||
|
|||||||
BIN
output_image.png
BIN
output_image.png
Binary file not shown.
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 1.7 KiB |
Reference in New Issue
Block a user