:py:mod:`kwcoco.demo.boids` =========================== .. py:module:: kwcoco.demo.boids Module Contents --------------- Classes ~~~~~~~ .. autoapisummary:: kwcoco.demo.boids.Boids Functions ~~~~~~~~~ .. autoapisummary:: kwcoco.demo.boids.clamp_mag kwcoco.demo.boids.triu_condense_multi_index kwcoco.demo.boids._spatial_index_scratch kwcoco.demo.boids.closest_point_on_line_segment kwcoco.demo.boids._pygame_render_boids kwcoco.demo.boids._yeah_boid .. py:class:: Boids(num, dims=2, rng=None, **kwargs) Bases: :py:obj:`ubelt.NiceRepr` Efficient numpy based backend for generating boid positions. BOID = bird-oid object .. rubric:: References https://www.youtube.com/watch?v=mhjuuHl6qHM https://medium.com/better-programming/boids-simulating-birds-flock-behavior-in-python-9fff99375118 https://en.wikipedia.org/wiki/Boids .. rubric:: Example >>> from kwcoco.demo.boids import * # NOQA >>> num_frames = 10 >>> num_objects = 3 >>> rng = None >>> self = Boids(num=num_objects, rng=rng).initialize() >>> paths = self.paths(num_frames) >>> # >>> # xdoctest: +REQUIRES(--show) >>> import kwplot >>> plt = kwplot.autoplt() >>> from mpl_toolkits.mplot3d import Axes3D # NOQA >>> ax = plt.gca(projection='3d') >>> ax.cla() >>> # >>> for path in paths: >>> time = np.arange(len(path)) >>> ax.plot(time, path.T[0] * 1, path.T[1] * 1, ',-') >>> ax.set_xlim(0, num_frames) >>> ax.set_ylim(-.01, 1.01) >>> ax.set_zlim(-.01, 1.01) >>> ax.set_xlabel('time') >>> ax.set_ylabel('u-pos') >>> ax.set_zlabel('v-pos') >>> kwplot.show_if_requested() import xdev _ = xdev.profile_now(self.compute_forces)() _ = xdev.profile_now(self.update_neighbors)() .. rubric:: Example >>> # Test determenism >>> from kwcoco.demo.boids import * # NOQA >>> num_frames = 2 >>> num_objects = 1 >>> rng = 4532 >>> self = Boids(num=num_objects, rng=rng).initialize() >>> #print(ub.hash_data(self.pos)) >>> #print(ub.hash_data(self.vel)) >>> #print(ub.hash_data(self.acc)) >>> tocheck = [] >>> for i in range(100): >>> self = Boids(num=num_objects, rng=rng).initialize() >>> self.step() >>> self.step() >>> self.step() >>> tocheck.append(self.pos.copy()) >>> assert ub.allsame(list(map(ub.hash_data, tocheck))) .. py:method:: __nice__(self) .. py:method:: initialize(self) .. py:method:: update_neighbors(self) .. py:method:: compute_forces(self) .. py:method:: boundary_conditions(self) .. py:method:: step(self) Update positions, velocities, and accelerations .. py:method:: paths(self, num_steps) .. py:function:: clamp_mag(vec, mag, axis=None) vec = np.random.rand(10, 2) mag = 1.0 axis = 1 new_vec = clamp_mag(vec, mag, axis) np.linalg.norm(new_vec, axis=axis) .. py:function:: triu_condense_multi_index(multi_index, dims, symetric=False) Like np.ravel_multi_index but returns positions in an upper triangular condensed square matrix .. rubric:: Examples multi_index (Tuple[ArrayLike]): indexes for each dimension into the square matrix dims (Tuple[int]): shape of each dimension in the square matrix (should all be the same) symetric (bool): if True, converts lower triangular indices to their upper triangular location. This may cause a copy to occur. .. rubric:: References https://stackoverflow.com/a/36867493/887074 https://numpy.org/doc/stable/reference/generated/numpy.ravel_multi_index.html#numpy.ravel_multi_index .. rubric:: Examples >>> dims = (3, 3) >>> symetric = True >>> multi_index = (np.array([0, 0, 1]), np.array([1, 2, 2])) >>> condensed_idxs = triu_condense_multi_index(multi_index, dims, symetric=symetric) >>> assert condensed_idxs.tolist() == [0, 1, 2] >>> n = 7 >>> symetric = True >>> multi_index = np.triu_indices(n=n, k=1) >>> condensed_idxs = triu_condense_multi_index(multi_index, [n] * 2, symetric=symetric) >>> assert condensed_idxs.tolist() == list(range(n * (n - 1) // 2)) >>> from scipy.spatial.distance import pdist, squareform >>> square_mat = np.zeros((n, n)) >>> conden_mat = squareform(square_mat) >>> conden_mat[condensed_idxs] = np.arange(len(condensed_idxs)) + 1 >>> square_mat = squareform(conden_mat) >>> print('square_mat =\n{}'.format(ub.repr2(square_mat, nl=1))) >>> n = 7 >>> symetric = True >>> multi_index = np.tril_indices(n=n, k=-1) >>> condensed_idxs = triu_condense_multi_index(multi_index, [n] * 2, symetric=symetric) >>> assert sorted(condensed_idxs.tolist()) == list(range(n * (n - 1) // 2)) >>> from scipy.spatial.distance import pdist, squareform >>> square_mat = np.zeros((n, n)) >>> conden_mat = squareform(square_mat, checks=False) >>> conden_mat[condensed_idxs] = np.arange(len(condensed_idxs)) + 1 >>> square_mat = squareform(conden_mat) >>> print('square_mat =\n{}'.format(ub.repr2(square_mat, nl=1))) .. py:function:: _spatial_index_scratch() .. py:function:: closest_point_on_line_segment(pts, e1, e2) Finds the closet point from p on line segment (e1, e2) :Parameters: * **pts** (*ndarray*) -- xy points [Nx2] * **e1** (*ndarray*) -- the first xy endpoint of the segment * **e2** (*ndarray*) -- the second xy endpoint of the segment :returns: pt_on_seg - the closest xy point on (e1, e2) from ptp :rtype: ndarray .. rubric:: References http://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line http://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment .. rubric:: Example >>> # ENABLE_DOCTEST >>> from kwcoco.demo.boids import * # NOQA >>> verts = np.array([[ 21.83012702, 13.16987298], >>> [ 16.83012702, 21.83012702], >>> [ 8.16987298, 16.83012702], >>> [ 13.16987298, 8.16987298], >>> [ 21.83012702, 13.16987298]]) >>> rng = np.random.RandomState(0) >>> pts = rng.rand(64, 2) * 20 + 5 >>> e1, e2 = verts[0:2] >>> closest_point_on_line_segment(pts, e1, e2) .. py:function:: _pygame_render_boids() Fast and responsive BOID rendering. This is an easter egg. Requirements: pip install pygame .. rubric:: CommandLine .. code-block:: bash python -m kwcoco.demo.boids pip install pygame kwcoco -U && python -m kwcoco.demo.boids .. py:function:: _yeah_boid()