Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added ConvexHull, ConvexHull3D, Label and LabeledPolygram #3933

Merged
merged 12 commits into from
Nov 27, 2024

Conversation

JayGupta797
Copy link
Contributor

@JayGupta797 JayGupta797 commented Sep 8, 2024

Overview: What does this pull request change?

This PR introduces four additional features and two utilities.

  1. manim.mobject.geometry.polygram.ConvexHull which constructs a ConvexHull through a set of points.
  2. manim.mobject.three\_d.polyhedra.ConvexHull3D which constructs a ConvexHull through a set of points.
  3. manim.mobject.geometry.labeled.Label which constructs a boxed label.
  4. manim.mobject.geometry.labeled.LabeledPolygram which constructs a polygram containing a label box at its pole of inaccessibility. This is the point inside a polygon that is farthest from its edges.
  5. manim.utilities.qhull which constructs a ConvexHull through a set of points. It extends to arbitrary dimensions.
  6. manim.utilities.polylabel which computes the pole of inaccessibility for any complex polygon to a given precision.

Notably, none of these introduce breaking changes nor require additional dependencies.

Motivation and Explanation: Why and how do your changes improve the library?

The first two are useful in scenarios where the user has a collection of points but does not know what order they should be plotted. In 2D this reduces to sorting by angle bit in 3D the task is more complex and can be handled automagically. The fourth is useful for labeling polygons.

Links to added or changed documentation pages

I am not sure how to format this, but here is what the doc pages look like locally:

  1. manim/docs/build/html/reference/manim.mobject.geometry.labeled.LabeledPolygram.html
  2. manim/docs/build/html/reference/manim.mobject.geometry.labeled.Label.html
  3. manim/docs/build/html/reference/manim.mobject.geometry.polygram.ConvexHull.html
  4. manim/docs/build/html/reference/manim.mobject.three_d.polyhedra.ConvexHull3D.html

Further Information and Comments

Reviewer Checklist

  • The PR title is descriptive enough for the changelog, and the PR is labeled correctly
  • If applicable: newly added non-private functions and classes have a docstring including a short summary and a PARAMETERS section
  • If applicable: newly added functions and classes are tested

manim/utils/polylabel.py Fixed Show fixed Hide fixed
manim/utils/qhull.py Fixed Show fixed Hide fixed
fixed added utils (i.e., Incomplete ordering and Explicit returns mixed with implicit), added :quality: high to docstrings, made ConvexHullExample determined
manim/utils/qhull.py Outdated Show resolved Hide resolved
manim/utils/qhull.py Outdated Show resolved Hide resolved
manim/utils/qhull.py Outdated Show resolved Hide resolved
Copy link
Contributor

@chopan050 chopan050 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your changes!
It is weird that the ReadTheDocs pipeline failed, even when you (and me) successfully ran make html locally. It sometimes fails randomly. Maybe if you push another commit it will run successfully.

I've taken a look at some of the code and left some suggestions. It's mainly about typing in polylabel.py and qhull.py.

manim/utils/qhull.py Fixed Show fixed Hide fixed
manim/utils/qhull.py Fixed Show fixed Hide fixed
added typing to `qhull.py` and `polylabel.py` for debugging. simplified test cases for `ConvexHull` and `ConvexHull3D` and rewrote control data. added tip to LabeledPolygon documentation.
manim/utils/polylabel.py Outdated Show resolved Hide resolved

Parameters
----------
rings : list[np.ndarray]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as before: the parameters should not be typed again in docstrings.

Suggested change
rings : list[np.ndarray]
rings

manim/utils/polylabel.py Outdated Show resolved Hide resolved
return self.d >= other.d


def PolyLabel(rings: list[list[Point3D]], precision: float = 0.01) -> Cell:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PascalCase is reserved for class definitions. For functions, use snake_case instead.

rings could be a Sequence[Point3D_Array] instead.

Suggested change
def PolyLabel(rings: list[list[Point3D]], precision: float = 0.01) -> Cell:
def polylabel(rings: Sequence[Point3D_Array], precision: float = 0.01) -> Cell:

manim/utils/polylabel.py Outdated Show resolved Hide resolved
manim/utils/qhull.py Outdated Show resolved Hide resolved
manim/utils/qhull.py Outdated Show resolved Hide resolved
manim/utils/qhull.py Outdated Show resolved Hide resolved
manim/utils/qhull.py Outdated Show resolved Hide resolved
manim/utils/qhull.py Outdated Show resolved Hide resolved
Comment on lines 95 to 98
self.outside: dict[Facet, tuple[np.ndarray, np.ndarray | None]] = {}
self.neighbors: dict[SubFacet, set[Facet]] = {}
self.unclaimed: np.ndarray | None = None
self.internal: np.ndarray | None = None
Copy link
Contributor

@chopan050 chopan050 Oct 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.outside: dict[Facet, tuple[np.ndarray, np.ndarray | None]] = {}
self.neighbors: dict[SubFacet, set[Facet]] = {}
self.unclaimed: np.ndarray | None = None
self.internal: np.ndarray | None = None
self.outside: dict[Facet, tuple[PointND_Array, PointND | None]] = {}
self.neighbors: dict[SubFacet, set[Facet]] = {}
self.unclaimed: PointND_Array | None = None
self.internal: PointND | None = None

self.outside[facet] = (outside, eye)
self.unclaimed = self.unclaimed[~mask]

def compute_horizon(self, eye: np.ndarray, start_facet: Facet) -> Horizon:
Copy link
Contributor

@chopan050 chopan050 Oct 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def compute_horizon(self, eye: np.ndarray, start_facet: Facet) -> Horizon:
def compute_horizon(self, eye: PointND, start_facet: Facet) -> Horizon:

return horizon

def _recursive_horizon(
self, eye: np.ndarray, facet: Facet, horizon: Horizon
Copy link
Contributor

@chopan050 chopan050 Oct 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self, eye: np.ndarray, facet: Facet, horizon: Horizon
self, eye: PointND, facet: Facet, horizon: Horizon

horizon.boundary.append(subfacet)
return True

def build(self, points: np.ndarray):
Copy link
Contributor

@chopan050 chopan050 Oct 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def build(self, points: np.ndarray):
def build(self, points: PointND_Array) -> None:

cy = np.sum((y + yr) * factor) / (6.0 * self.area)
self.centroid = np.array([cx, cy])

def compute_distance(self, point: np.ndarray) -> float:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def compute_distance(self, point: np.ndarray) -> float:
def compute_distance(self, point: PointND) -> float:

d = np.min(np.linalg.norm(self.start + self.diff * clips - point, axis=1))
return d if self.inside(point) else -d

def inside(self, point: np.ndarray) -> bool:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def inside(self, point: np.ndarray) -> bool:
def inside(self, point: PointND) -> bool:

Copy link
Contributor

@chopan050 chopan050 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello! Sorry for the delay on reviewing this PR. Here are the changes I propose this time. Thanks for publishing this PR!

On a second thought, I feel that implementing PointND and PointND_Array becomes more and more necessary for us as I continue reviewing this PR, so, if possible, I would like to ask you to implement them. Otherwise, in order to have a separate PR making that change instead, I could add them in another PR.

import numpy as np


class Point:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There already exists a Point mobject in Manim: it's manim.mobject.types.point_cloud_mobject.Point. Whether we should actually keep this Point in Manim or not is pretty debatable, but, in the meantime, let's change this class' name to something else to avoid name clashes.

Since this is merely an auxiliar class which is only used in this file, maybe it could be prefixed with an underscore, or be called QuickHullPoint.

Suggested change
class Point:
class QuickHullPoint:

@chopan050
Copy link
Contributor

Hello there! I just added the PointND and PointND_Array type aliases.

JayGupta797 and others added 4 commits November 25, 2024 22:23
added `label_config` and `box_config` and `frame_config` for additional configuration options and cleaner interface. added `InternalPointND` and `PointND ` and `InternalPointND_Array` and `PointND_Array` for typing. updated docstring.
@JayGupta797
Copy link
Contributor Author

Hello there! Apologies for the delay here. Do let me know if any additional changes are needed. Also, not sure how to handle the pre-commit typing issue with self.color in shape_matchers.py since I didn't make that change.

Copy link
Contributor

@chopan050 chopan050 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your time! There's a small batch of comments I left, but they should be the last ones before we ship this!

manim/mobject/geometry/labeled.py Outdated Show resolved Hide resolved
manim/mobject/geometry/labeled.py Outdated Show resolved Hide resolved
Comment on lines 67 to 69
label_config: dict | None = None,
box_config: dict | None = None,
frame_config: dict | None = None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using dict[str, Any] instead of just dict is preferable here for two reasons:

  • The keys of dict can be any immutable object, but here we require that these config dictionaries have str keys.
  • Even if we had dict[Any, Any], we would prefer to be explicit anyways.
Suggested change
label_config: dict | None = None,
box_config: dict | None = None,
frame_config: dict | None = None,
label_config: dict[str, Any] | None = None,
box_config: dict[str, Any] | None = None,
frame_config: dict[str, Any] | None = None,

Comment on lines 343 to 345
label_config: dict | None = None,
box_config: dict | None = None,
frame_config: dict | None = None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
label_config: dict | None = None,
box_config: dict | None = None,
frame_config: dict | None = None,
label_config: dict[str, Any] | None = None,
box_config: dict[str, Any] | None = None,
frame_config: dict[str, Any] | None = None,

Label that will be displayed.
label_config
A dictionary containing the configuration for the label.
This is only applied if `label` is of type `str`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This is only applied if `label` is of type `str`.
This is only applied if ``label`` is of type ``str``.

:class:`LabeledArrow`
label_config
A dictionary containing the configuration for the label.
This is only applied if `label` is of type `str`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This is only applied if `label` is of type `str`.
This is only applied if ``label`` is of type ``str``.

Determine the opacity of the label box by passing a value in the range [0-1], where 0 indicates complete transparency and 1 means full opacity.
label_config
A dictionary containing the configuration for the label.
This is only applied if `label` is of type `str`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This is only applied if `label` is of type `str`.
This is only applied if ``label`` is of type ``str``.

Parameters
----------
vertex_groups
Vertices passed to Polygram constructor.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A reference to the Polygram superclass would be great for our documentation:

Suggested change
Vertices passed to Polygram constructor.
Vertices passed to the :class:`~.Polygram` constructor.

@@ -122,6 +122,7 @@ def __init__(
buff=buff,
**kwargs,
)
self.color: ManimColor
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't worry about this shape_matchers issue: it was introduced in a different PR, and it would be solved in another one.

Suggested change
self.color: ManimColor

from manim.typing import PointND, PointND_Array


class Point:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, don't forget about this one!

Suggested change
class Point:
class QuickHullPoint:

@chopan050 chopan050 changed the title Added ConvexHull and LabeledPolygram Added ConvexHull, ConvexHull3D, Label and LabeledPolygram Nov 27, 2024
@chopan050 chopan050 enabled auto-merge (squash) November 27, 2024 00:31
Copy link
Contributor

@chopan050 chopan050 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thank you for your time.

@chopan050 chopan050 merged commit 862504f into ManimCommunity:main Nov 27, 2024
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

2 participants