Перемещайте точки по их собственному пути

#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()