[英]How to order points for Matplotlib's fill function?
多邊形由兩個列表表示。 列表xs
包含按降序排列的x坐標。 對於每個x值,列表regions
的對應元素是切片列表。 每個切片表示屬於多邊形內部的y范圍。 對於每個x值,切片將按升序排序。
我想使用Matplotlib的fill
函數繪制填充的多邊形。 該函數需要對點進行排序,以便從點到點描述多邊形的輪廓。 如何排序數據集中的點以獲得正確的多邊形?
這是我擁有的數據類型的示例。
xs = range(10, 0, -1)
regions = [[slice(0, 3)],
[slice(0, 4), slice(5.2, 5.8)],
[slice(1, 5), slice(5.4, 5.8)],
[slice(1.3, 6)],
[slice(1.8, 6)],
[slice(1.9, 3), slice(3.1, 6.1)],
[slice(2, 2.9), slice(3.2, 5), slice(6, 6.2)],
[slice(2.1, 2.7), slice(3.4, 5.1), slice(5.9, 6.3)],
[slice(3.5, 5.2), slice(5.7, 6.4)],
[slice(3.8, 5.3), slice(5.8, 6.1)]]
訂購積分的正確方法如下
xx = [10, 9, 8, 7, 6, 5, 4, 3, 3, 4, 5, 5, 4, 3, 2, 1,
1, 2, 3, 4, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8,
9, 9, 8, 8, 9, 10]
yy = [0, 0, 1, 1.3, 1.8, 1.9, 2, 2.1, 2.7, 2.9, 3, 3.1,
3.2, 3.4, 3.5, 3.8, 5.3, 5.2, 5.1, 5, 6, 5.9, 5.7,
5.8, 6.1, 6.4, 6.3, 6.2, 6.3, 6, 6, 5.8, 5.8, 5.2,
5.4, 5, 4, 3]
這個數字應該看起來像這樣
到目前為止,我已經嘗試定義轉折點 ,即輪廓改變方向的x值。 這可以通過將numpy.diff
應用於包含每個x值的切片數量的數組來完成。 如果差不為零,則該x值為轉折點。 這可能可以用來找出下一點。 困難在於確定下一個切片以及使用切片的開始還是結束。
這個問題是相似的,但是在我的情況下,多邊形的形狀要復雜得多。 另一方面,我確實有關於多邊形內部內容的信息。
編輯
我只是意識到,上述問題並不總是有獨特的解決方案。 例如,上面示例中的點集也接受了解決方案
xx = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 5,
4, 3, 3, 4, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8,
9, 9, 8, 8, 9, 10]
yy = [0, 0, 1, 1.3, 1.8, 1.9, 2, 2.1, 3.5, 3.8, 5.3, 5.2,
2.7, 2.9, 3, 3.1, 3.2, 3.4, 5.1, 5, 6, 5.9, 5.7,
5.8, 6.1, 6.4, 6.3, 6.2, 6.1, 6, 6, 5.8, 5.8, 5.2,
5.4, 5, 4, 3]
我正在尋找的解決方案是使每個邊緣的斜率(垂直部分除外)的絕對值最小的解決方案。 在上面的示例中,這對應於第一個解決方案。
這是完成工作的功能。 它是一種生成器,可產生找到的所有多邊形(如果有多個連接的多邊形)。
首先,必須將區域重寫為元組:
regions = [[(sl.start, sl.stop) for sl in sllist] for sllist in regions]
通過調用生成器(循環或其他方式)來完成排序。 該函數產生一個多邊形,作為(x,y)點的列表。 獲得單獨的xx
和yy
列表很容易:
for polygon in order_points(xs, regions):
xx, yy = zip(*polygon)
該函數本身已被詳細注釋。
def order_points(x, regions):
"""Given two lists of the same length, one of x values and one of regions,
determine how to order the points so that they describe a polygon that
contains the interior of all regions.
This function is a generator that yields disjoint polygons.
Parameters
==========
x: list
x values at which the regions are defined
regions: list of lists of tuples
Each list contains information for one x value. The sublist contains
so-called regions that are described by tuples corresponding to the
bottom and the top of the region. The y values between the bottom and
the top of each region are in the interior of a polygon.
Each list of tuples should be sorted by increasing y value.
Yields
======
polygon: list of tuples
Ordered list of (x, y) coordinated of the points on the polygon.
"""
# Make sure the x values are in ascending order.
xvals, yregions = zip(*sorted(zip(x, regions)))
# Direction is -1 when going toward low x, 1 when going toward high x.
direction = 1
# Indicate whether the inside of the polygon is below, 0, or above, 1, the
# current point.
inside = 1
# List all possible combinations of x index, region index.
tovisit = [(pos, rpos) for pos in range(len(xvals))
for rpos in range(len(yregions[pos]))]
pos, rpos = tovisit.pop(0)
ycur = yregions[pos][rpos][0]
# Keep track of already visited points.
visited = set()
# Keep track of current polygon.
polygon = []
while True:
# Keep track of ordered points.
xcur = xvals[pos]
polygon.append((xcur, ycur))
visited.add((xcur, ycur))
# Find the minimum vertical distance between the current position and
# the following points: points at the next (as specified by direction)
# x value, and points at the current x value
# For points at the next x, if the polygon is currently above, inside =
# 1, the points considered are at the bottom of a region, i.e., at
# index 0 of the tuple.
next_pos = pos + direction
if next_pos < 0 or next_pos >= len(xvals):
next_pos = pos
distance = -1
for ri, region in enumerate(yregions[pos]):
if (xcur, region[inside]) not in visited:
d = abs(ycur - region[inside])
if d < distance or distance == -1:
distance = d
move = ('vertical', (pos, ri, inside))
# For points at the same x, if the polygon is currently above, the
# points considered are at the top of a region, i.e., at index 1 of the
# tuple.
for ri, region in enumerate(yregions[next_pos]):
polypos = (not inside)
if (xvals[next_pos], region[polypos]) not in visited:
d = abs(ycur - region[polypos])
if d < distance or distance == -1:
distance = d
move = ('next', (next_pos, ri, polypos))
# If no suitable next point is found, the polygon is complete. Yield
# the polygon and try to find a separate polygon.
if distance < 0:
yield polygon
polygon = []
direction = 10 # dummy value to detect if no next point is found
while tovisit:
pos, slpos = tovisit.pop(0)
ycur = yregions[pos][slpos][0]
if (xvals[pos], ycur) not in visited:
direction = 1
inside = 1
break
if direction == 10:
break
continue
# Make the move.
if move[0] == 'vertical':
# Vertical move changes the direction (unless it is the very first
# move) and toggles inside/outside.
if len(polygon) > 1:
direction *= -1
inside = int(not inside)
pos, rpos, end = move[1]
ycur = yregions[pos][rpos][end]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.