1 /*
<lambda>null2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.ink.geometry
18 
19 import androidx.annotation.FloatRange
20 import androidx.annotation.IntRange
21 import androidx.annotation.RestrictTo
22 import androidx.annotation.VisibleForTesting
23 import androidx.ink.geometry.internal.threadLocal
24 import androidx.ink.nativeloader.NativeLoader
25 import androidx.ink.nativeloader.UsedByNative
26 
27 /**
28  * An immutable** complex shape expressed as a set of triangles. This is used to represent the shape
29  * of a stroke or other complex objects. The mesh may be divided into multiple partitions, which
30  * enables certain brush effects (e.g. "multi-coat"), and allows ink to create strokes using greater
31  * than 2^16 triangles (which must be rendered in multiple passes).
32  *
33  * A [PartitionedMesh] may optionally have one or more "outlines", which are polylines that traverse
34  * some or all of the vertices in the mesh; these are used for path-based rendering of strokes. This
35  * supports disjoint meshes such as dashed lines.
36  *
37  * [PartitionedMesh] provides fast intersection and coverage testing by use of an internal spatial
38  * index.
39  *
40  * ** [PartitionedMesh] is technically not immutable, as the spatial index is lazily instantiated;
41  * however, from the perspective of a caller, its properties do not change over the course of its
42  * lifetime. The entire object is thread-safe.
43  */
44 @Suppress("NotCloseable") // Finalize is only used to free the native peer.
45 public class PartitionedMesh
46 private constructor(
47     /**
48      * This is the raw pointer address of an `ink::PartitionedMesh` that has been heap allocated to
49      * be owned solely by this JVM [PartitionedMesh] object. Although the `ink::PartitionedMesh` is
50      * owned exclusively by this [PartitionedMesh] object, it may be a copy of another
51      * `ink::PartitionedMesh`, where it has a copy of fairly lightweight metadata but shares
52      * ownership of the more heavyweight `ink::Mesh` objects. This class is responsible for freeing
53      * the `ink::PartitionedMesh` through its [finalize] method.
54      */
55     @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public val nativePointer: Long
56 ) {
57 
58     private val scratchIntArray by threadLocal { IntArray(2) }
59 
60     /**
61      * Only for tests - creates a new empty [PartitionedMesh]. Since a [PartitionedMesh] is
62      * immutable, this serves no practical purpose outside of tests.
63      */
64     @VisibleForTesting internal constructor() : this(PartitionedMeshNative.create())
65 
66     /**
67      * Returns the number of render groups in this mesh. Each outline in the [PartitionedMesh]
68      * belongs to exactly one render group, which are numbered in z-order: the group with index zero
69      * should be rendered on bottom; the group with the highest index should be rendered on top.
70      */
71     @IntRange(from = 0)
72     public fun getRenderGroupCount(): Int =
73         PartitionedMeshNative.getRenderGroupCount(nativePointer).also { check(it >= 0) }
74 
75     /** The [Mesh] objects that make up this shape. */
76     private val meshesByGroup: List<List<Mesh>> =
77         (0 until getRenderGroupCount()).map { groupIndex ->
78             PartitionedMeshNative.newCopiesOfMeshes(nativePointer, groupIndex).map(Mesh::wrapNative)
79         }
80 
81     private var _bounds: Box? = null
82 
83     /**
84      * Returns the minimum bounding box of the [PartitionedMesh]. This will be null if the
85      * [PartitionedMesh] is empty.
86      */
87     public fun computeBoundingBox(): Box? {
88         // If we've already computed the bounding box, re-use it -- it won't change over the
89         // lifetime of
90         // this object.
91         if (_bounds != null) return _bounds
92 
93         // If we have no meshes, then the bounding box is null.
94         if (meshesByGroup.isEmpty()) return null
95 
96         val envelope = BoxAccumulator()
97         for (meshes in meshesByGroup) {
98             for (mesh in meshes) {
99                 envelope.add(mesh.bounds)
100             }
101         }
102         _bounds = envelope.box
103         return envelope.box
104     }
105 
106     /** Returns the [MeshFormat] used for each [Mesh] in the specified render group. */
107     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // NonPublicApi
108     public fun renderGroupFormat(@IntRange(from = 0) groupIndex: Int): MeshFormat {
109         require(groupIndex >= 0 && groupIndex < getRenderGroupCount()) {
110             "groupIndex=$groupIndex must be between 0 and getRenderGroupCount()=${getRenderGroupCount()}"
111         }
112         return MeshFormat(PartitionedMeshNative.getRenderGroupFormat(nativePointer, groupIndex))
113     }
114 
115     /**
116      * Returns the meshes that make up render group [groupIndex], listed in z-order (the first mesh
117      * in the span should be rendered on bottom; the last mesh should be rendered on top).
118      */
119     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // NonPublicApi
120     public fun renderGroupMeshes(@IntRange(from = 0) groupIndex: Int): List<Mesh> {
121         require(groupIndex >= 0 && groupIndex < getRenderGroupCount()) {
122             "groupIndex=$groupIndex must be between 0 and getRenderGroupCount()=${getRenderGroupCount()}"
123         }
124         return meshesByGroup[groupIndex]
125     }
126 
127     /**
128      * Returns the number of outlines of the mesh for the render group at [groupIndex].
129      *
130      * Groups with discontinuous geometry will always have multiple outlines, but even continuous
131      * geometry may be drawn with multiple overlapping outlines when this improves rendering quality
132      * or performance.
133      */
134     @IntRange(from = 0)
135     public fun getOutlineCount(@IntRange(from = 0) groupIndex: Int): Int {
136         require(groupIndex >= 0 && groupIndex < getRenderGroupCount()) {
137             "groupIndex=$groupIndex must be between 0 and getRenderGroupCount()=${getRenderGroupCount()}"
138         }
139         return PartitionedMeshNative.getOutlineCount(nativePointer, groupIndex).also {
140             check(it >= 0)
141         }
142     }
143 
144     /**
145      * Returns the number of vertices that are in the outline at [outlineIndex] in the render group
146      * at [groupIndex].
147      */
148     @IntRange(from = 0)
149     public fun getOutlineVertexCount(
150         @IntRange(from = 0) groupIndex: Int,
151         @IntRange(from = 0) outlineIndex: Int,
152     ): Int {
153         require(outlineIndex >= 0 && outlineIndex < getOutlineCount(groupIndex)) {
154             "outlineIndex=$outlineIndex must be between 0 and getOutlineCount=${getOutlineCount(groupIndex)}"
155         }
156         return PartitionedMeshNative.getOutlineVertexCount(nativePointer, groupIndex, outlineIndex)
157             .also { check(it >= 0) }
158     }
159 
160     /**
161      * Populates [outPosition] with the position of the outline vertex at [outlineVertexIndex] in
162      * the outline at [outlineIndex] in the render group at [groupIndex], and returns [outPosition].
163      * [groupIndex] must be less than [getRenderGroupCount], [outlineIndex] must be less
164      * [getOutlineVertexCount] for [groupIndex], and [outlineVertexIndex] must be less than
165      * [getOutlineVertexCount] for [groupIndex] and [outlineIndex].
166      */
167     public fun populateOutlinePosition(
168         @IntRange(from = 0) groupIndex: Int,
169         @IntRange(from = 0) outlineIndex: Int,
170         @IntRange(from = 0) outlineVertexIndex: Int,
171         outPosition: MutableVec,
172     ): MutableVec {
173         val outlineVertexCount = getOutlineVertexCount(groupIndex, outlineIndex)
174         require(outlineVertexIndex >= 0 && outlineVertexIndex < outlineVertexCount) {
175             "outlineVertexIndex=$outlineVertexIndex must be between 0 and " +
176                 "outlineVertexCount($outlineVertexIndex)=$outlineVertexCount"
177         }
178         PartitionedMeshNative.fillOutlineMeshIndexAndMeshVertexIndex(
179             nativePointer,
180             groupIndex,
181             outlineIndex,
182             outlineVertexIndex,
183             scratchIntArray,
184         )
185         val (meshIndex, meshVertexIndex) = scratchIntArray
186         val mesh = meshesByGroup[groupIndex][meshIndex]
187         mesh.fillPosition(meshVertexIndex, outPosition)
188         return outPosition
189     }
190 
191     /**
192      * Computes an approximate measure of what portion of this [PartitionedMesh] is covered by or
193      * overlaps with [triangle]. This is calculated by finding the sum of areas of the triangles
194      * that intersect the given [triangle], and dividing that by the sum of the areas of all
195      * triangles in the [PartitionedMesh], all in the [PartitionedMesh]'s coordinate space.
196      * Triangles in the [PartitionedMesh] that overlap each other (e.g. in the case of a stroke that
197      * loops back over itself) are counted individually. Note that, if any triangles have negative
198      * area (due to winding, see [Triangle.computeSignedArea]), the absolute value of their area
199      * will be used instead.
200      *
201      * On an empty [PartitionedMesh], this will always return 0.
202      *
203      * Optional argument [triangleToThis] contains the transform that maps from [triangle]'s
204      * coordinate space to this [PartitionedMesh]'s coordinate space, which defaults to
205      * [AffineTransform.IDENTITY].
206      */
207     @JvmOverloads
208     @FloatRange(from = 0.0, to = 1.0)
209     public fun computeCoverage(
210         triangle: Triangle,
211         triangleToThis: AffineTransform = AffineTransform.IDENTITY,
212     ): Float =
213         PartitionedMeshNative.partitionedMeshTriangleCoverage(
214             nativePointer = nativePointer,
215             triangleP0X = triangle.p0.x,
216             triangleP0Y = triangle.p0.y,
217             triangleP1X = triangle.p1.x,
218             triangleP1Y = triangle.p1.y,
219             triangleP2X = triangle.p2.x,
220             triangleP2Y = triangle.p2.y,
221             triangleToThisTransformA = triangleToThis.m00,
222             triangleToThisTransformB = triangleToThis.m10,
223             triangleToThisTransformC = triangleToThis.m20,
224             triangleToThisTransformD = triangleToThis.m01,
225             triangleToThisTransformE = triangleToThis.m11,
226             triangleToThisTransformF = triangleToThis.m21,
227         )
228 
229     /**
230      * Computes an approximate measure of what portion of this [PartitionedMesh] is covered by or
231      * overlaps with [box]. This is calculated by finding the sum of areas of the triangles that
232      * intersect the given [box], and dividing that by the sum of the areas of all triangles in the
233      * [PartitionedMesh], all in the [PartitionedMesh]'s coordinate space. Triangles in the
234      * [PartitionedMesh] that overlap each other (e.g. in the case of a stroke that loops back over
235      * itself) are counted individually. Note that, if any triangles have negative area (due to
236      * winding, see [Triangle.computeSignedArea]), the absolute value of their area will be used
237      * instead.
238      *
239      * On an empty [PartitionedMesh], this will always return 0.
240      *
241      * Optional argument [boxToThis] contains the transform that maps from [box]'s coordinate space
242      * to this [PartitionedMesh]'s coordinate space, which defaults to [AffineTransform.IDENTITY].
243      */
244     @JvmOverloads
245     @FloatRange(from = 0.0, to = 1.0)
246     public fun computeCoverage(
247         box: Box,
248         boxToThis: AffineTransform = AffineTransform.IDENTITY,
249     ): Float =
250         PartitionedMeshNative.partitionedMeshBoxCoverage(
251             nativePointer = nativePointer,
252             boxXMin = box.xMin,
253             boxYMin = box.yMin,
254             boxXMax = box.xMax,
255             boxYMax = box.yMax,
256             boxToThisTransformA = boxToThis.m00,
257             boxToThisTransformB = boxToThis.m10,
258             boxToThisTransformC = boxToThis.m20,
259             boxToThisTransformD = boxToThis.m01,
260             boxToThisTransformE = boxToThis.m11,
261             boxToThisTransformF = boxToThis.m21,
262         )
263 
264     /**
265      * Computes an approximate measure of what portion of this [PartitionedMesh] is covered by or
266      * overlaps with [parallelogram]. This is calculated by finding the sum of areas of the
267      * triangles that intersect the given [parallelogram], and dividing that by the sum of the areas
268      * of all triangles in the [PartitionedMesh], all in the [PartitionedMesh]'s coordinate space.
269      * Triangles in the [PartitionedMesh] that overlap each other (e.g. in the case of a stroke that
270      * loops back over itself) are counted individually. Note that, if any triangles have negative
271      * area (due to winding, see [Triangle.computeSignedArea]), the absolute value of their area
272      * will be used instead.
273      *
274      * On an empty [PartitionedMesh], this will always return 0.
275      *
276      * Optional argument [parallelogramToThis] contains the transform that maps from
277      * [parallelogram]'s coordinate space to this [PartitionedMesh]'s coordinate space, which
278      * defaults to [AffineTransform.IDENTITY].
279      */
280     @JvmOverloads
281     @FloatRange(from = 0.0, to = 1.0)
282     public fun computeCoverage(
283         parallelogram: Parallelogram,
284         parallelogramToThis: AffineTransform = AffineTransform.IDENTITY,
285     ): Float =
286         PartitionedMeshNative.partitionedMeshParallelogramCoverage(
287             nativePointer = nativePointer,
288             parallelogramCenterX = parallelogram.center.x,
289             parallelogramCenterY = parallelogram.center.y,
290             parallelogramWidth = parallelogram.width,
291             parallelogramHeight = parallelogram.height,
292             parallelogramAngleInRadian = parallelogram.rotation,
293             parallelogramShearFactor = parallelogram.shearFactor,
294             parallelogramToThisTransformA = parallelogramToThis.m00,
295             parallelogramToThisTransformB = parallelogramToThis.m10,
296             parallelogramToThisTransformC = parallelogramToThis.m20,
297             parallelogramToThisTransformD = parallelogramToThis.m01,
298             parallelogramToThisTransformE = parallelogramToThis.m11,
299             parallelogramToThisTransformF = parallelogramToThis.m21,
300         )
301 
302     /**
303      * Computes an approximate measure of what portion of this [PartitionedMesh] is covered by or
304      * overlaps with the [other] [PartitionedMesh]. This is calculated by finding the sum of areas
305      * of the triangles that intersect [other], and dividing that by the sum of the areas of all
306      * triangles in the [PartitionedMesh], all in the [PartitionedMesh]'s coordinate space.
307      * Triangles in the [PartitionedMesh] that overlap each other (e.g. in the case of a stroke that
308      * loops back over itself) are counted individually. Note that, if any triangles have negative
309      * area (due to winding, see [Triangle.computeSignedArea]), the absolute value of their area
310      * will be used instead.
311      *
312      * On an empty [PartitionedMesh], this will always return 0.
313      *
314      * Optional argument [otherShapeToThis] contains the transform that maps from [other]'s
315      * coordinate space to this [PartitionedMesh]'s coordinate space, which defaults to
316      * [AffineTransform.IDENTITY].
317      */
318     @JvmOverloads
319     @FloatRange(from = 0.0, to = 1.0)
320     public fun computeCoverage(
321         other: PartitionedMesh,
322         otherShapeToThis: AffineTransform = AffineTransform.IDENTITY,
323     ): Float =
324         PartitionedMeshNative.partitionedMeshPartitionedMeshCoverage(
325             nativePointer = nativePointer,
326             otherShapeNativePointer = other.nativePointer,
327             otherShapeToThisTransformA = otherShapeToThis.m00,
328             otherShapeToThisTransformB = otherShapeToThis.m10,
329             otherShapeToThisTransformC = otherShapeToThis.m20,
330             otherShapeToThisTransformD = otherShapeToThis.m01,
331             otherShapeToThisTransformE = otherShapeToThis.m11,
332             otherShapeToThisTransformF = otherShapeToThis.m21,
333         )
334 
335     /**
336      * Returns true if the approximate portion of the [PartitionedMesh] covered by [triangle] is
337      * greater than [coverageThreshold].
338      *
339      * This is equivalent to:
340      * ```
341      * computeCoverage(triangle, triangleToThis) > coverageThreshold
342      * ```
343      *
344      * but may be faster.
345      *
346      * On an empty [PartitionedMesh], this will always return 0.
347      *
348      * Optional argument [triangleToThis] contains the transform that maps from [triangle]'s
349      * coordinate space to this [PartitionedMesh]'s coordinate space, which defaults to
350      * [AffineTransform.IDENTITY].
351      */
352     @JvmOverloads
353     public fun computeCoverageIsGreaterThan(
354         triangle: Triangle,
355         coverageThreshold: Float,
356         triangleToThis: AffineTransform = AffineTransform.IDENTITY,
357     ): Boolean =
358         PartitionedMeshNative.partitionedMeshTriangleCoverageIsGreaterThan(
359             nativePointer = nativePointer,
360             triangleP0X = triangle.p0.x,
361             triangleP0Y = triangle.p0.y,
362             triangleP1X = triangle.p1.x,
363             triangleP1Y = triangle.p1.y,
364             triangleP2X = triangle.p2.x,
365             triangleP2Y = triangle.p2.y,
366             coverageThreshold = coverageThreshold,
367             triangleToThisTransformA = triangleToThis.m00,
368             triangleToThisTransformB = triangleToThis.m10,
369             triangleToThisTransformC = triangleToThis.m20,
370             triangleToThisTransformD = triangleToThis.m01,
371             triangleToThisTransformE = triangleToThis.m11,
372             triangleToThisTransformF = triangleToThis.m21,
373         )
374 
375     /**
376      * Returns true if the approximate portion of the [PartitionedMesh] covered by [box] is greater
377      * than [coverageThreshold].
378      *
379      * This is equivalent to:
380      * ```
381      * computeCoverage(box, boxToThis) > coverageThreshold
382      * ```
383      *
384      * but may be faster.
385      *
386      * On an empty [PartitionedMesh], this will always return 0.
387      *
388      * Optional argument [boxToThis] contains the transform that maps from [box]'s coordinate space
389      * to this [PartitionedMesh]'s coordinate space, which defaults to [AffineTransform.IDENTITY].
390      */
391     @JvmOverloads
392     public fun computeCoverageIsGreaterThan(
393         box: Box,
394         coverageThreshold: Float,
395         boxToThis: AffineTransform = AffineTransform.IDENTITY,
396     ): Boolean =
397         PartitionedMeshNative.partitionedMeshBoxCoverageIsGreaterThan(
398             nativePointer = nativePointer,
399             boxXMin = box.xMin,
400             boxYMin = box.yMin,
401             boxXMax = box.xMax,
402             boxYMax = box.yMax,
403             coverageThreshold = coverageThreshold,
404             boxToThisTransformA = boxToThis.m00,
405             boxToThisTransformB = boxToThis.m10,
406             boxToThisTransformC = boxToThis.m20,
407             boxToThisTransformD = boxToThis.m01,
408             boxToThisTransformE = boxToThis.m11,
409             boxToThisTransformF = boxToThis.m21,
410         )
411 
412     /**
413      * Returns true if the approximate portion of the [PartitionedMesh] covered by [parallelogram]
414      * is greater than [coverageThreshold].
415      *
416      * This is equivalent to:
417      * ```
418      * computeCoverage(parallelogram, parallelogramToThis) > coverageThreshold
419      * ```
420      *
421      * but may be faster.
422      *
423      * On an empty [PartitionedMesh], this will always return 0.
424      *
425      * Optional argument [parallelogramToThis] contains the transform that maps from
426      * [parallelogram]'s coordinate space to this [PartitionedMesh]'s coordinate space, which
427      * defaults to [AffineTransform.IDENTITY].
428      */
429     @JvmOverloads
430     public fun computeCoverageIsGreaterThan(
431         parallelogram: Parallelogram,
432         coverageThreshold: Float,
433         parallelogramToThis: AffineTransform = AffineTransform.IDENTITY,
434     ): Boolean =
435         PartitionedMeshNative.partitionedMeshParallelogramCoverageIsGreaterThan(
436             nativePointer = nativePointer,
437             parallelogramCenterX = parallelogram.center.x,
438             parallelogramCenterY = parallelogram.center.y,
439             parallelogramWidth = parallelogram.width,
440             parallelogramHeight = parallelogram.height,
441             parallelogramAngleInRadian = parallelogram.rotation,
442             parallelogramShearFactor = parallelogram.shearFactor,
443             coverageThreshold = coverageThreshold,
444             parallelogramToThisTransformA = parallelogramToThis.m00,
445             parallelogramToThisTransformB = parallelogramToThis.m10,
446             parallelogramToThisTransformC = parallelogramToThis.m20,
447             parallelogramToThisTransformD = parallelogramToThis.m01,
448             parallelogramToThisTransformE = parallelogramToThis.m11,
449             parallelogramToThisTransformF = parallelogramToThis.m21,
450         )
451 
452     /**
453      * Returns true if the approximate portion of this [PartitionedMesh] covered by the [other]
454      * [PartitionedMesh] is greater than [coverageThreshold].
455      *
456      * This is equivalent to:
457      * ```
458      * computeCoverage(other, otherShapeToThis) > coverageThreshold
459      * ```
460      *
461      * but may be faster.
462      *
463      * On an empty [PartitionedMesh], this will always return 0.
464      *
465      * Optional argument [otherShapeToThis] contains the transform that maps from [other]'s
466      * coordinate space to this [PartitionedMesh]'s coordinate space, which defaults to
467      * [AffineTransform.IDENTITY].
468      */
469     @JvmOverloads
470     public fun computeCoverageIsGreaterThan(
471         other: PartitionedMesh,
472         coverageThreshold: Float,
473         otherShapeToThis: AffineTransform = AffineTransform.IDENTITY,
474     ): Boolean =
475         PartitionedMeshNative.partitionedMeshPartitionedMeshCoverageIsGreaterThan(
476             nativePointer = nativePointer,
477             otherShapeNativePointer = other.nativePointer,
478             coverageThreshold = coverageThreshold,
479             otherShapeToThisTransformA = otherShapeToThis.m00,
480             otherShapeToThisTransformB = otherShapeToThis.m10,
481             otherShapeToThisTransformC = otherShapeToThis.m20,
482             otherShapeToThisTransformD = otherShapeToThis.m01,
483             otherShapeToThisTransformE = otherShapeToThis.m11,
484             otherShapeToThisTransformF = otherShapeToThis.m21,
485         )
486 
487     /**
488      * Initializes this MutableEnvelope's spatial index for geometry queries. If a geometry query is
489      * made with this shape and the spatial index is not currently initialized, it will be
490      * initialized in real time to satisfy that query.
491      */
492     public fun initializeSpatialIndex(): Unit =
493         PartitionedMeshNative.initializeSpatialIndex(nativePointer)
494 
495     /** Returns true if this MutableEnvelope's spatial index has been initialized. */
496     @VisibleForTesting
497     internal fun isSpatialIndexInitialized(): Boolean =
498         PartitionedMeshNative.isSpatialIndexInitialized(nativePointer)
499 
500     override fun toString(): String {
501         val address = java.lang.Long.toHexString(nativePointer)
502         return "PartitionedMesh(bounds=${computeBoundingBox()}, meshesByGroup=$meshesByGroup, " +
503             "nativePointer=$address)"
504     }
505 
506     protected fun finalize() {
507         // NOMUTANTS--Not tested post garbage collection.
508         PartitionedMeshNative.free(nativePointer)
509     }
510 
511     /** Declared as a target for extension functions. */
512     public companion object {
513         /**
514          * Construct a [PartitionedMesh] from an unowned heap-allocated native pointer to a C++
515          * `PartitionedMesh`.
516          */
517         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
518         public fun wrapNative(unownedNativePointer: Long): PartitionedMesh =
519             PartitionedMesh(unownedNativePointer)
520     }
521 }
522 
523 /** Helper object to contain native JNI calls. */
524 @UsedByNative
525 private object PartitionedMeshNative {
526 
527     init {
528         NativeLoader.load()
529     }
530 
createnull531     @UsedByNative external fun create(): Long
532 
533     @UsedByNative external fun free(nativePointer: Long)
534 
535     @UsedByNative external fun newCopiesOfMeshes(nativePointer: Long, groupIndex: Int): LongArray
536 
537     @UsedByNative external fun getRenderGroupCount(nativePointer: Long): Int
538 
539     @UsedByNative external fun getRenderGroupFormat(nativePointer: Long, groupIndex: Int): Long
540 
541     @UsedByNative external fun getOutlineCount(nativePointer: Long, groupIndex: Int): Int
542 
543     @UsedByNative
544     external fun getOutlineVertexCount(nativePointer: Long, groupIndex: Int, outlineIndex: Int): Int
545 
546     @UsedByNative
547     external fun fillOutlineMeshIndexAndMeshVertexIndex(
548         nativePointer: Long,
549         groupIndex: Int,
550         outlineIndex: Int,
551         outlineVertexIndex: Int,
552         outMeshIndexAndMeshVertexIndex: IntArray,
553     )
554 
555     /**
556      * JNI method to construct C++ `PartitionedMesh` and `Triangle` objects and calculate coverage
557      * using them.
558      */
559     @UsedByNative
560     external fun partitionedMeshTriangleCoverage(
561         nativePointer: Long,
562         triangleP0X: Float,
563         triangleP0Y: Float,
564         triangleP1X: Float,
565         triangleP1Y: Float,
566         triangleP2X: Float,
567         triangleP2Y: Float,
568         triangleToThisTransformA: Float,
569         triangleToThisTransformB: Float,
570         triangleToThisTransformC: Float,
571         triangleToThisTransformD: Float,
572         triangleToThisTransformE: Float,
573         triangleToThisTransformF: Float,
574     ): Float
575 
576     /**
577      * JNI method to construct C++ `PartitionedMesh` and `Triangle` objects and calculate coverage
578      * using them.
579      */
580     @UsedByNative
581     external fun partitionedMeshBoxCoverage(
582         nativePointer: Long,
583         boxXMin: Float,
584         boxYMin: Float,
585         boxXMax: Float,
586         boxYMax: Float,
587         boxToThisTransformA: Float,
588         boxToThisTransformB: Float,
589         boxToThisTransformC: Float,
590         boxToThisTransformD: Float,
591         boxToThisTransformE: Float,
592         boxToThisTransformF: Float,
593     ): Float
594 
595     /**
596      * JNI method to construct C++ `PartitionedMesh` and `Quad` objects and calculate coverage using
597      * them.
598      */
599     @UsedByNative
600     external fun partitionedMeshParallelogramCoverage(
601         nativePointer: Long,
602         parallelogramCenterX: Float,
603         parallelogramCenterY: Float,
604         parallelogramWidth: Float,
605         parallelogramHeight: Float,
606         parallelogramAngleInRadian: Float,
607         parallelogramShearFactor: Float,
608         parallelogramToThisTransformA: Float,
609         parallelogramToThisTransformB: Float,
610         parallelogramToThisTransformC: Float,
611         parallelogramToThisTransformD: Float,
612         parallelogramToThisTransformE: Float,
613         parallelogramToThisTransformF: Float,
614     ): Float
615 
616     /**
617      * JNI method to construct C++ two `PartitionedMesh` objects and calculate coverage using them.
618      */
619     @UsedByNative
620     external fun partitionedMeshPartitionedMeshCoverage(
621         nativePointer: Long,
622         otherShapeNativePointer: Long,
623         otherShapeToThisTransformA: Float,
624         otherShapeToThisTransformB: Float,
625         otherShapeToThisTransformC: Float,
626         otherShapeToThisTransformD: Float,
627         otherShapeToThisTransformE: Float,
628         otherShapeToThisTransformF: Float,
629     ): Float
630 
631     /**
632      * JNI method to construct C++ `PartitionedMesh` and `Triangle` objects and call native
633      * `CoverageIsGreaterThan` on them.
634      */
635     @UsedByNative
636     external fun partitionedMeshTriangleCoverageIsGreaterThan(
637         nativePointer: Long,
638         triangleP0X: Float,
639         triangleP0Y: Float,
640         triangleP1X: Float,
641         triangleP1Y: Float,
642         triangleP2X: Float,
643         triangleP2Y: Float,
644         coverageThreshold: Float,
645         triangleToThisTransformA: Float,
646         triangleToThisTransformB: Float,
647         triangleToThisTransformC: Float,
648         triangleToThisTransformD: Float,
649         triangleToThisTransformE: Float,
650         triangleToThisTransformF: Float,
651     ): Boolean
652 
653     /**
654      * JNI method to construct C++ `PartitionedMesh` and `Rect` objects and call native
655      * `CoverageIsGreaterThan` on them.
656      */
657     @UsedByNative
658     external fun partitionedMeshBoxCoverageIsGreaterThan(
659         nativePointer: Long,
660         boxXMin: Float,
661         boxYMin: Float,
662         boxXMax: Float,
663         boxYMax: Float,
664         coverageThreshold: Float,
665         boxToThisTransformA: Float,
666         boxToThisTransformB: Float,
667         boxToThisTransformC: Float,
668         boxToThisTransformD: Float,
669         boxToThisTransformE: Float,
670         boxToThisTransformF: Float,
671     ): Boolean
672 
673     /**
674      * JNI method to construct C++ `PartitionedMesh` and `Quad` objects and call native
675      * `CoverageIsGreaterThan` on them.
676      */
677     @UsedByNative
678     external fun partitionedMeshParallelogramCoverageIsGreaterThan(
679         nativePointer: Long,
680         parallelogramCenterX: Float,
681         parallelogramCenterY: Float,
682         parallelogramWidth: Float,
683         parallelogramHeight: Float,
684         parallelogramAngleInRadian: Float,
685         parallelogramShearFactor: Float,
686         coverageThreshold: Float,
687         parallelogramToThisTransformA: Float,
688         parallelogramToThisTransformB: Float,
689         parallelogramToThisTransformC: Float,
690         parallelogramToThisTransformD: Float,
691         parallelogramToThisTransformE: Float,
692         parallelogramToThisTransformF: Float,
693     ): Boolean
694 
695     /**
696      * JNI method to construct two C++ `PartitionedMesh` objects and call native
697      * `CoverageIsGreaterThan` on them.
698      */
699     @UsedByNative
700     external fun partitionedMeshPartitionedMeshCoverageIsGreaterThan(
701         nativePointer: Long,
702         otherShapeNativePointer: Long,
703         coverageThreshold: Float,
704         otherShapeToThisTransformA: Float,
705         otherShapeToThisTransformB: Float,
706         otherShapeToThisTransformC: Float,
707         otherShapeToThisTransformD: Float,
708         otherShapeToThisTransformE: Float,
709         otherShapeToThisTransformF: Float,
710     ): Boolean
711 
712     @UsedByNative external fun initializeSpatialIndex(nativePointer: Long)
713 
714     @UsedByNative external fun isSpatialIndexInitialized(nativePointer: Long): Boolean
715 }
716