Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Speedup triangulation of edges (napari#7268)
# References and relevant issues # Description Similar to napari#7256 this PR is a speedup of face triangulation by avoiding a lot of reallocation of arrays. After long investigation, I found that using best way to speed this up is by avoiding the allocation of additional arrays by using numba. For shapes that do not have bevels: On main: ![obraz](https://github.com/user-attachments/assets/b6775d94-a88b-4a99-959f-f4b6f7f768aa) In this PR: ![obraz](https://github.com/user-attachments/assets/9a3a302c-2eb1-4ecd-8c38-a07617512ede) PR with cache: ![obraz](https://github.com/user-attachments/assets/6d7f00a8-4d55-4bb2-9f49-955bc746e01f) As you may see, the time of `triangulate_edge` is reduced from 44s to 17s. Half of it is numba compilation. With sharp edges that require bevel join, the speedup is even bigger: Main: ![obraz](https://github.com/user-attachments/assets/e2a55f13-6ba7-4f1d-9ff9-56fa46fdf49c) PR: ![obraz](https://github.com/user-attachments/assets/6279350e-1e50-441e-a538-4f01b5420d40) PR with cache: ![obraz](https://github.com/user-attachments/assets/3c992928-b63c-42d7-be6d-d0ec7ec620f5) This PR also fixes the bevel join On main: ![obraz](https://github.com/user-attachments/assets/9a24bc31-9777-4d60-81d5-ff6cc1b0d1a2) With PR: ![obraz](https://github.com/user-attachments/assets/b167a4d5-9bce-442c-80c6-d5a0c90e5f8d) In this PR I have added `examples/dev/triangle_edge.py` with a set of helper functions to visualize triangulation. These are helpful for debugging of triangulation problems. # Algorithm explanation Here I add some explanation to the code implementation of this PR. ## Calculation of miter vector <a href="#user-content-miter" id="miter">#</a> This is a screenshot of miter join: ![obraz](https://github.com/user-attachments/assets/08d2dd78-bb02-4da8-baa5-7b0596e1c3c7) And let's assume markings as on image: ![obraz](https://github.com/user-attachments/assets/d0cec9ff-3786-4edc-805a-d45e58f33aab) The $\vec{BA}$ (blue) is half of the normal vector from vertex $n$ to $n+1$. The $\vec{AC}$ is minus half of the normal vector from vertex $n-1$ to $n$. The vector $\vec{AD}$ (green) is orthogonal to line $BA$ of length $0.5$ Based on these vectors, we could easily calculate $\vec{BC} = \vec{BA} + \vec{AC}$. However, the length of $|GC|$ may be different from 1. We would like to get the vector $\vec{BE}$ We will use here trigonometry and [Intercept theorem](https://en.wikipedia.org/wiki/Intercept_theorem) We know that $\frac{|GC|}{|AC|} = \sin{\alpha}$ and $|AC| = \frac{1}{2}$ so $|GC| = \frac{\sin{\alpha}}{2}$ From intercept theorem, we know that $\frac{|BC|}{|GC|} = \frac{|BE|}{|FE|}$. The length of $|FE| = \frac{1}{2}$. So $|BE| = |BC| \times \frac{1}{\sin{\alpha}}$. That mens that $\vec{BE} = \vec{BC} \times \frac{1}{\sin{\alpha}}$ ## Bevel limit <a href="#user-content-bevel-limit" id="bevel-limit">#</a> In general, we use miter join, however, if the angle is sharp, it may result in strange artifacts (see napari#6706). So if the join will be too long (The length of $\vec{BA}$ vector from above), we would like to change to a bevel join. So we need to calculate the length of $\vec{BE}$. We would like to avoid calculation of square root in every turn of the loop. From Pythagoras theorem we know that $|BE|^2 = |EF|^2 + |BF|^2 = \frac{1}{2}^2 + |BF|^2$ We also know that $|GA| = \frac{\cos{\alpha}}{2}$ so $|BG| = \frac{1 - \cos{\alpha}}{2}$. From miter vector calculation and intercept theorem we know that $|BF| = \frac{|BG|}{\sin{\alpha}} = \frac{1 - \cos{\alpha}}{2\sin{\alpha}}$ So we know the length of the vector $$|BE|^2 = \frac{1}{2}^2 + \left(\frac{1 - \cos{\alpha}}{2\sin{\alpha}}\right)^2 = \frac{\sin^2{\alpha} + 1^2 - 2\cos{\alpha} + \cos^2{\alpha}}{4\sin^2{\alpha}} = \frac{2 - 2 \cos{\alpha}}{4\sin^2{\alpha}} $$ Using trigonometric identity $$|BE|^2 = \frac{1}{2} \cdot \frac{1 - \cos{\alpha}}{1 - \cos^2{\alpha}} = \frac{1}{2} \cdot \frac{1 - \cos{\alpha}}{(1 - \cos{\alpha})(1 + \cos{\alpha})} = \frac{1}{2} \cdot \frac{1}{1+\cos{\alpha}}$$ So if we would like to check condition $|BE| > limit$ we could use the following equation: $$ \frac{1}{2} \cdot \frac{1}{1+\cos{\alpha}} > {limit}^2$$ So: $$ \frac{1}{2 \cdot {limit}^2} - 1 > \cos{\alpha} $$ It means that if $\cos\alpha$ is below a given threshold, we should use bevel join. ## Too long vector in bevel join <a href="#user-content-bevel-cut" id="bevel-cut">#</a> As I showed above, this PR is fixing the bevel join by fixing the position of inner edge of the bevel join. You may see an example in this image (see the longest yellow vector): ![obraz](https://github.com/user-attachments/assets/58e13821-a1b6-4dd0-a49b-ade97edf9e73) The schematic of generating long edge: ![obraz](https://github.com/user-attachments/assets/329b2b61-3c9d-4050-9623-7053c1f46248) As users may use shapes with different length edges, we cannot use a hard-coded limit for the length of the offset vector that creates the problems pointed on above image. However, when calculating the normal vectors, we need to calculate the edge width, so we may save them for later usage and limit the length of such problematic offset vector to length of shortest adjusted edge. In the Bevel limit section, we have an explicit formula for miter offset length. However, it contains a square root. And calculation of a square root is expensive on current CPU, so we would like to avoid it. However, the described problem happens only when $\measuredangle BAC$ is close to $180\degree$, but in such a situation, the $|\vec{BA} + \vec{AC}|$ is close to 1 (as resulted vector is close to diameter). So we could approximate the length of vector $\vec{BC}$ as 1. Then the estimated length of vector $\vec{BE}$ is $\frac{1}{\sin{\alpha}}$ So if $\frac{1}{\sin{\alpha}}$ is bigger than the length of half of any adjusted edges, we could calculate a new scaling factor with value $$\pm 0.5 \cdot\min(vec1\textunderscore{}len, vec2\textunderscore{}len))$$ # Additional notes ## Artifacts There are still artifacts in rendering ![obraz](https://github.com/user-attachments/assets/416df018-f293-425c-bd38-916ba8137934) --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Carol Willing <[email protected]> Co-authored-by: Juan Nunez-Iglesias <[email protected]>
- Loading branch information