#python #numpy
Вопрос:
У меня есть список подключенных точек.
Я хочу переместить точки/белые квадраты по их собственным путям, вот так:
Мне нравится иметь версию numpy, но я не уверен, что это возможно.
Так что подход на python тоже был бы хорош.
Конечным результатом должен быть список новых точек.
Комментарии:
1. Итак, у вас есть набор точек, которые определяют кривую. Вы хотите переместить эти точки вдоль кривой, которую они ПЕРВОНАЧАЛЬНО сформировали? Основная процедура достаточно проста. Вы знаете, на каком отрезке линии лежит каждая точка. Вы можете использовать простую линейную интерполяцию, чтобы найти его следующее местоположение на этом отрезке линии. Вам просто нужно следить за точками, пересекающимися на следующем отрезке линии.
2. Это, по-видимому, связано с событиями, управляемыми щелчком мыши, которые немного выходят за рамки пакета numpy (который является сторонней библиотекой python, так как я не уверен, что это понятно). Но поскольку у вас есть коллекция линейных сегментов, вы можете параметризовать шаговую функцию
f(t) = (x(t), y(t))
и определить точкиt
, в которых она переключается с одного линейного сегмента на другой.3. Кроме того, пожалуйста, добавьте то, что вы пробовали до сих пор, чтобы люди могли лучше помочь вам.
Ответ №1:
from typing import List import numpy as np class _LineSegment: def __init__(self, v1: np.ndarray, v2: np.ndarray) -gt; None: """Helper representation of a line segment. Makes use of PolygonialPath's data integrity (i.e. no further range checks) and implements handling of v1 == v2 case. """ self.v1 = v1 self.v2 = v2 v_delta = v2 - v1 self.length = np.linalg.norm(v_delta) self.direction = v_delta / self.length if self.length else v_delta class PolygonalChain: def __init__(self, vertices: List[np.ndarray]) -gt; None: if len(vertices) lt; 2: raise ValueError("Trivial polygonial paths not supported!") v_shape = vertices[0].shape if len(v_shape) != 1: raise ValueError("Vertices have to be represented as vectors!") if any(v.shape != v_shape for v in vertices): raise ValueError("Vertices have to be of consistent length!") self._vertices = vertices self._vertices_arclen = [0] self._strict_vertices_arclen = [0] self._strict_segments = [] v_prev = self._vertices[0] for v_next in vertices[1:]: segment = _LineSegment(v_prev, v_next) self._vertices_arclen.append(self._vertices_arclen[-1] segment.length) if segment.length: self._strict_vertices_arclen.append(self._vertices_arclen[-1]) self._strict_segments.append(segment) v_prev = v_next if not self._strict_segments: raise ValueError("Trivial polygonial paths not supported!") # for convenience in `point_at`, set last value to inf (to handle # extrapolation beyond last point) self._strict_vertices_arclen[-1] = np.inf def vertices_arclen(self) -gt; List[float]: """For each vertice, its arc-length along the path.""" return self._vertices_arclen def point_at(self, arclen: float) -gt; np.ndarray: """Provide point corresponding to the arc-length. Extrapolation done by first and last segment. """ # map to line segment (could be optimized by bi-section) segment_idx = 0 while arclen gt; self._strict_vertices_arclen[segment_idx 1]: segment_idx = 1 # compute point segment = self._strict_segments[segment_idx] arclen_on_segment = arclen - self._strict_vertices_arclen[segment_idx] return segment.v1 arclen_on_segment * segment.direction def example() -gt; None: from matplotlib import pyplot as plt # some data pts = [ np.array((1, 2)), np.array((1, 2)), np.array((3, 5)), np.array((4, 7)), np.array((4, 8)), np.array((3, 6)), ] # polygonial chain representation chain = PolygonalChain(pts) pts_arclen = chain.vertices_arclen() # apply some step delta_arclen = -0.5 pts_shifted = [chain.point_at(p_arclen delta_arclen) for p_arclen in pts_arclen] # plot plt.plot([p[0] for p in pts], [p[1] for p in pts], "- k") plt.plot([p[0] for p in pts_shifted], [p[1] for p in pts_shifted], "r*") plt.show() if __name__ == "__main__": example()
Комментарии:
1. Что мне нужно изменить, чтобы сделать переключение циклическим? (замкнутая многоугольная цепочка)
2. Вы можете сделать это с текущей реализацией, установив последнюю точку на первую и используя только значения дуги в диапазоне (используйте мод).
Ответ №2:
Вот кое-что, что заставит вас задуматься. Я определяю набор точек. Я вычисляю длину и угол линейных сегментов (обратите внимание, что это не удастся для вертикальных сегментов). Затем я запускаю анимацию matplotlib, отображающую исходные сегменты линий и текущие точки. Тебе этого недостаточно, потому что я не собираюсь огибать углы. Точки просто продолжают двигаться в том же направлении. Упражнение, оставленное для читателя?
import math import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation # Define a set of points that make up a rough circle. curve = np.array(( ( 0, 0 ), ( 8, 2 ), ( 12, 7 ), ( 5, 11 ), ( -2, 13 ), ( -4, 8 ), ( -2, 4 ), ( 0, 0 ) )) # Cmpute angle and length of segment. segments = [] for c in range(len(curve)-1): delta = curve[c 1]-curve[c] length = math.sqrt(delta[0]*delta[0] delta[1]*delta[1]) angle = math.atan2(delta[1], delta[0]) segments.append( (delta, length, angle) ) # Step 0.5 units per frame. STEP = 0.5 def movePoint( pt, segment ): # Move pt down the line between prv and nxt. ptx = math.cos(segment[2]) * STEP pty = math.sin(segment[2]) * STEP return pt (ptx,pty) def advance(points, curve): newpts = [] for i in range(len(curve)-1): newpts.append( movePoint( points[i], segments[i]) ) return np.array(newpts) fig, ax = plt.subplots() plt.plot( curve[:,0], curve[:,1] ) def init(): global points global ln points = curve.copy() ln = plt.scatter( points[:,0], points[:,1] ) return ln, def update(frame): global points points = advance(points, curve) ln.set_offsets( points ) return ln, ani = FuncAnimation( fig, update, frames=12, init_func=init, blit=True ) plt.show()