lyon_tessellation/geometry_builder.rs
1//! Tools to help with generating vertex and index buffers.
2//!
3//! ## Overview
4//!
5//! While it would be possible for the tessellation algorithms to manually generate vertex
6//! and index buffers with a certain layout, it would mean that most code using the tessellators
7//! have to copy and convert all generated vertices in order to have their own vertex
8//! layout, or de-interleaved vertex formats, which is a very common use-case.
9//!
10//! In order to flexibly and efficiently build geometry of various flavors, this module contains
11//! a number of builder interfaces that centered around the idea of building vertex and index
12//! buffers without having to know about the final vertex and index types.
13//!
14//! See:
15//!
16//! * [`GeometryBuilder`](trait.GeometryBuilder.html)
17//! * [`FillGeometryBuilder`](trait.FillGeometryBuilder.html)
18//! * [`StrokeGeometryBuilder`](trait.StrokeGeometryBuilder.html)
19//!
20//! The traits above are what the tessellators interface with. It is very common to push
21//! vertices and indices into a pair of vectors, so to facilitate this pattern this module
22//! also provides:
23//!
24//! * The struct [`VertexBuffers`](struct.VertexBuffers.html) is a simple pair of vectors of
25//! indices and vertices (generic parameters).
26//! * The struct [`BuffersBuilder`](struct.BuffersBuilder.html) which writes into a
27//! [`VertexBuffers`](struct.VertexBuffers.html) and implements the various geometry
28//! builder traits. It takes care of filling the buffers while producing vertices is
29//! delegated to a vertex constructor.
30//! * The traits [`FillVertexConstructor`](trait.FillVertexConstructor.html),
31//! [`StrokeVertexConstructor`](trait.StrokeVertexConstructor.html) and
32//! [`BuffersBuilder`](struct.BuffersBuilder.html) in order to generate any vertex type. In the
33//! first example below, a struct `WithColor` implements the `FillVertexConstructor` trait in order to
34//! create vertices composed of a 2d position and a color value from an input 2d position.
35//! This separates the construction of vertex values from the assembly of the vertex buffers.
36//! Another, simpler example of vertex constructor is the [`Positions`](struct.Positions.html)
37//! constructor which just returns the vertex position untransformed.
38//!
39//! Geometry builders are a practical way to add one last step to the tessellation pipeline,
40//! such as applying a transform or clipping the geometry.
41//!
42//! While this is module designed to facilitate the generation of vertex buffers and index
43//! buffers, nothing prevents a given GeometryBuilder implementation to only generate a
44//! vertex buffer without indices, or write into a completely different format.
45//! These builder traits are at the end of the tessellation pipelines and are meant for
46//! users of this crate to be able to adapt the output of the tessellators to their own
47//! needs.
48//!
49//! ## Do I need to implement geometry builders or vertex constructors?
50//!
51//! If you only generate a vertex buffer and an index buffer (as a pair of standard `Vec`),
52//! then the simplest option is to work with custom vertex constructors and use
53//! `VertexBuffers` and `BuffersBuilder`.
54//!
55//! For more specific or elaborate use cases where control over where the vertices as written
56//! is needed such as building de-interleaved vertex buffers or writing directly into a mapped
57//! GPU buffer, implementing custom geometry builders is the right thing to do.
58//!
59//! Which of the vertex constructor or geometry builder traits to implement (fill/stroke/basic
60//! variants), depends on which tessellators the builder or constructor will interface with.
61//!
62//! ## Examples
63//!
64//! ### Generating custom vertices
65//!
66//! The example below implements the `FillVertexConstructor` trait in order to use a custom
67//! vertex type `MyVertex` (containing position and color), storing the tessellation in a
68//! `VertexBuffers<MyVertex, u16>`, and tessellates two shapes with different colors.
69//!
70//! ```
71//! extern crate lyon_tessellation as tess;
72//! use tess::{FillVertexConstructor, VertexBuffers, BuffersBuilder, FillOptions, FillTessellator, FillVertex};
73//! use tess::math::{Point, point};
74//!
75//! // Our custom vertex.
76//! #[derive(Copy, Clone, Debug)]
77//! pub struct MyVertex {
78//! position: [f32; 2],
79//! color: [f32; 4],
80//! }
81//!
82//! // The vertex constructor. This is the object that will be used to create the custom
83//! // vertices from the information provided by the tessellators.
84//! struct WithColor([f32; 4]);
85//!
86//! impl FillVertexConstructor<MyVertex> for WithColor {
87//! fn new_vertex(&mut self, vertex: FillVertex) -> MyVertex {
88//! MyVertex {
89//! position: vertex.position().to_array(),
90//! color: self.0,
91//! }
92//! }
93//! }
94//!
95//! fn main() {
96//! let mut output: VertexBuffers<MyVertex, u16> = VertexBuffers::new();
97//! let mut tessellator = FillTessellator::new();
98//! // Tessellate a red and a green circle.
99//! tessellator.tessellate_circle(
100//! point(0.0, 0.0),
101//! 10.0,
102//! &FillOptions::tolerance(0.05),
103//! &mut BuffersBuilder::new(
104//! &mut output,
105//! WithColor([1.0, 0.0, 0.0, 1.0])
106//! ),
107//! );
108//! tessellator.tessellate_circle(
109//! point(10.0, 0.0),
110//! 5.0,
111//! &FillOptions::tolerance(0.05),
112//! &mut BuffersBuilder::new(
113//! &mut output,
114//! WithColor([0.0, 1.0, 0.0, 1.0])
115//! ),
116//! );
117//!
118//! println!(" -- {} vertices, {} indices", output.vertices.len(), output.indices.len());
119//! }
120//! ```
121//!
122//! ### Generating a completely custom output
123//!
124//! Using `VertexBuffers<T>` is convenient and probably fits a lot of use cases, but
125//! what if we do not want to write the geometry in a pair of vectors?
126//! Perhaps we want to write the geometry in a different data structure or directly
127//! into gpu-accessible buffers mapped on the CPU?
128//!
129//! ```
130//! extern crate lyon_tessellation as tess;
131//! use tess::{StrokeTessellator, GeometryBuilder, StrokeGeometryBuilder, StrokeOptions, GeometryBuilderError, StrokeVertex, VertexId};
132//! use tess::math::{Point, point};
133//! use tess::path::polygon::Polygon;
134//! use std::fmt::Debug;
135//! use std::u32;
136//!
137//! // A geometry builder that writes the result of the tessellation to stdout instead
138//! // of filling vertex and index buffers.
139//! pub struct ToStdOut {
140//! vertices: u32,
141//! indices: u32,
142//! }
143//!
144//! impl ToStdOut {
145//! pub fn new() -> Self { ToStdOut { vertices: 0, indices: 0 } }
146//! }
147//!
148//! impl GeometryBuilder for ToStdOut {
149//! fn begin_geometry(&mut self) {
150//! // Reset the vertex in index counters.
151//! self.vertices = 0;
152//! self.indices = 0;
153//! println!(" -- begin geometry");
154//! }
155//!
156//! fn add_triangle(&mut self, a: VertexId, b: VertexId, c: VertexId) {
157//! println!("triangle ({}, {}, {})", a.offset(), b.offset(), c.offset());
158//! self.indices += 3;
159//! }
160//!
161//! fn abort_geometry(&mut self) {
162//! println!(" -- oops!");
163//! }
164//! }
165//!
166//! impl StrokeGeometryBuilder for ToStdOut {
167//! fn add_stroke_vertex(&mut self, vertex: StrokeVertex) -> Result<VertexId, GeometryBuilderError> {
168//! println!("vertex {:?}", vertex.position());
169//! if self.vertices >= u32::MAX {
170//! return Err(GeometryBuilderError::TooManyVertices);
171//! }
172//! self.vertices += 1;
173//! Ok(VertexId(self.vertices as u32 - 1))
174//! }
175//! }
176//!
177//! fn main() {
178//! let mut output = ToStdOut::new();
179//! let mut tessellator = StrokeTessellator::new();
180//! tessellator.tessellate_polygon(
181//! Polygon {
182//! points: &[point(0.0, 0.0), point(10.0, 0.0), point(5.0, 5.0)],
183//! closed: true,
184//! },
185//! &StrokeOptions::default(),
186//! &mut output,
187//! );
188//! }
189//! ```
190//!
191
192pub use crate::error::GeometryBuilderError;
193use crate::math::Point;
194use crate::{FillVertex, Index, StrokeVertex, VertexId};
195
196use alloc::vec::Vec;
197use core::convert::From;
198use core::ops::Add;
199
200/// An interface separating tessellators and other geometry generation algorithms from the
201/// actual vertex construction.
202///
203/// Depending on which tessellator a geometry builder interfaces with, it also has to
204/// implement one or several of the following traits (Which contain the hooks to generate
205/// vertices):
206/// - [`FillGeometryBuilder`](trait.FillGeometryBuilder.html)
207/// - [`StrokeGeometryBuilder`](trait.StrokeGeometryBuilder.html)
208///
209/// See the [`geometry_builder`](index.html) module documentation for more detailed explanation.
210pub trait GeometryBuilder {
211 /// Called at the beginning of a generation.
212 ///
213 /// end_geometry must be called before begin_geometry is called again.
214 fn begin_geometry(&mut self) {}
215
216 /// Called at the end of a generation.
217 /// Returns the number of vertices and indices added since the last time begin_geometry was
218 /// called.
219 fn end_geometry(&mut self) {}
220
221 /// Insert a triangle made of vertices that were added after the last call to begin_geometry.
222 ///
223 /// This method can only be called between begin_geometry and end_geometry.
224 fn add_triangle(&mut self, a: VertexId, b: VertexId, c: VertexId);
225
226 /// abort_geometry is called instead of end_geometry if an error occurred while producing
227 /// the geometry and we won't be able to finish.
228 ///
229 /// The implementation is expected to discard the geometry that was generated since the last
230 /// time begin_geometry was called, and to remain in a usable state.
231 fn abort_geometry(&mut self) {}
232}
233
234/// A Geometry builder to interface with the [`FillTessellator`](../struct.FillTessellator.html).
235///
236/// Types implementing this trait must also implement the [`GeometryBuilder`](trait.GeometryBuilder.html) trait.
237pub trait FillGeometryBuilder: GeometryBuilder {
238 /// Inserts a vertex, providing its position, and optionally a normal.
239 /// Returns a vertex id that is only valid between begin_geometry and end_geometry.
240 ///
241 /// This method can only be called between begin_geometry and end_geometry.
242 fn add_fill_vertex(&mut self, vertex: FillVertex) -> Result<VertexId, GeometryBuilderError>;
243}
244
245/// A Geometry builder to interface with the [`StrokeTessellator`](../struct.StrokeTessellator.html).
246///
247/// Types implementing this trait must also implement the [`GeometryBuilder`](trait.GeometryBuilder.html) trait.
248pub trait StrokeGeometryBuilder: GeometryBuilder {
249 /// Inserts a vertex, providing its position, and optionally a normal.
250 /// Returns a vertex id that is only valid between begin_geometry and end_geometry.
251 ///
252 /// This method can only be called between begin_geometry and end_geometry.
253 fn add_stroke_vertex(&mut self, vertex: StrokeVertex)
254 -> Result<VertexId, GeometryBuilderError>;
255}
256
257/// Structure that holds the vertex and index data.
258///
259/// Usually written into though temporary `BuffersBuilder` objects.
260#[derive(Clone, Debug, Default)]
261#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
262pub struct VertexBuffers<OutputVertex, OutputIndex> {
263 pub vertices: Vec<OutputVertex>,
264 pub indices: Vec<OutputIndex>,
265}
266
267impl<OutputVertex, OutputIndex> VertexBuffers<OutputVertex, OutputIndex> {
268 /// Constructor
269 pub fn new() -> Self {
270 VertexBuffers::with_capacity(512, 1024)
271 }
272
273 /// Constructor
274 pub fn with_capacity(num_vertices: usize, num_indices: usize) -> Self {
275 VertexBuffers {
276 vertices: Vec::with_capacity(num_vertices),
277 indices: Vec::with_capacity(num_indices),
278 }
279 }
280
281 /// Empty the buffers without freeing memory, for reuse without reallocation.
282 pub fn clear(&mut self) {
283 self.vertices.clear();
284 self.indices.clear();
285 }
286}
287
288/// A temporary view on a `VertexBuffers` object which facilitate the population of vertex and index
289/// data.
290///
291/// `BuffersBuilders` record the vertex offset from when they are created so that algorithms using
292/// them don't need to worry about offsetting indices if some geometry was added beforehand. This
293/// means that from the point of view of a `BuffersBuilder` user, the first added vertex is at always
294/// offset at the offset 0 and `VertexBuilder` takes care of translating indices adequately.
295///
296/// Often, algorithms are built to generate vertex positions without knowledge of eventual other
297/// vertex vertex. The `VertexConstructor` does the translation from generic `Input` to `OutputVertex`.
298/// If your logic generates the actual vertex type directly, you can use the `SimpleBuffersBuilder`
299/// convenience typedef.
300pub struct BuffersBuilder<'l, OutputVertex: 'l, OutputIndex: 'l, Ctor> {
301 buffers: &'l mut VertexBuffers<OutputVertex, OutputIndex>,
302 first_vertex: Index,
303 first_index: Index,
304 vertex_offset: Index,
305 vertex_constructor: Ctor,
306}
307
308impl<'l, OutputVertex: 'l, OutputIndex: 'l, Ctor>
309 BuffersBuilder<'l, OutputVertex, OutputIndex, Ctor>
310{
311 pub fn new(buffers: &'l mut VertexBuffers<OutputVertex, OutputIndex>, ctor: Ctor) -> Self {
312 let first_vertex = buffers.vertices.len() as Index;
313 let first_index = buffers.indices.len() as Index;
314 BuffersBuilder {
315 buffers,
316 first_vertex,
317 first_index,
318 vertex_offset: 0,
319 vertex_constructor: ctor,
320 }
321 }
322
323 pub fn with_vertex_offset(mut self, offset: Index) -> Self {
324 self.vertex_offset = offset;
325
326 self
327 }
328
329 /// Consumes self and returns a builder with opposite triangle face winding.
330 pub fn with_inverted_winding(self) -> InvertWinding<Self> {
331 InvertWinding(self)
332 }
333
334 pub fn buffers<'a, 'b: 'a>(&'b self) -> &'a VertexBuffers<OutputVertex, OutputIndex> {
335 self.buffers
336 }
337
338 fn add_vertex_impl(&mut self, vertex: OutputVertex) -> Result<VertexId, GeometryBuilderError>
339 where
340 OutputIndex: MaxIndex
341 {
342 let len = self.buffers.vertices.len();
343 let id = len
344 .checked_add(self.vertex_offset as usize)
345 .filter(|i| *i <= OutputIndex::MAX)
346 .ok_or(GeometryBuilderError::TooManyVertices)?;
347
348 self.buffers.vertices.push(vertex);
349 Ok(VertexId(id as Index))
350 }
351}
352
353/// A wrapper for stroke and fill geometry builders that inverts the triangle face winding.
354pub struct InvertWinding<B>(B);
355
356impl<B: GeometryBuilder> GeometryBuilder for InvertWinding<B> {
357 fn begin_geometry(&mut self) {
358 self.0.begin_geometry();
359 }
360
361 fn end_geometry(&mut self) {
362 self.0.end_geometry()
363 }
364
365 fn add_triangle(&mut self, a: VertexId, b: VertexId, c: VertexId) {
366 // Invert the triangle winding by flipping b and c.
367 self.0.add_triangle(a, c, b);
368 }
369
370 fn abort_geometry(&mut self) {
371 self.0.abort_geometry();
372 }
373}
374
375impl<B: FillGeometryBuilder> FillGeometryBuilder for InvertWinding<B> {
376 #[inline]
377 fn add_fill_vertex(&mut self, vertex: FillVertex) -> Result<VertexId, GeometryBuilderError> {
378 self.0.add_fill_vertex(vertex)
379 }
380}
381
382impl<B: StrokeGeometryBuilder> StrokeGeometryBuilder for InvertWinding<B> {
383 #[inline]
384 fn add_stroke_vertex(
385 &mut self,
386 vertex: StrokeVertex,
387 ) -> Result<VertexId, GeometryBuilderError> {
388 self.0.add_stroke_vertex(vertex)
389 }
390}
391
392/// A trait specifying how to create vertex values.
393pub trait FillVertexConstructor<OutputVertex> {
394 fn new_vertex(&mut self, vertex: FillVertex) -> OutputVertex;
395}
396
397/// A trait specifying how to create vertex values.
398pub trait StrokeVertexConstructor<OutputVertex> {
399 fn new_vertex(&mut self, vertex: StrokeVertex) -> OutputVertex;
400}
401
402/// A simple vertex constructor that just takes the position.
403pub struct Positions;
404
405impl FillVertexConstructor<Point> for Positions {
406 fn new_vertex(&mut self, vertex: FillVertex) -> Point {
407 vertex.position()
408 }
409}
410
411impl StrokeVertexConstructor<Point> for Positions {
412 fn new_vertex(&mut self, vertex: StrokeVertex) -> Point {
413 vertex.position()
414 }
415}
416
417impl<F, OutputVertex> FillVertexConstructor<OutputVertex> for F
418where
419 F: Fn(FillVertex) -> OutputVertex,
420{
421 fn new_vertex(&mut self, vertex: FillVertex) -> OutputVertex {
422 self(vertex)
423 }
424}
425
426impl<F, OutputVertex> StrokeVertexConstructor<OutputVertex> for F
427where
428 F: Fn(StrokeVertex) -> OutputVertex,
429{
430 fn new_vertex(&mut self, vertex: StrokeVertex) -> OutputVertex {
431 self(vertex)
432 }
433}
434
435/// A `BuffersBuilder` that takes the actual vertex type as input.
436pub type SimpleBuffersBuilder<'l> = BuffersBuilder<'l, Point, u16, Positions>;
437
438/// Creates a `SimpleBuffersBuilder`.
439pub fn simple_builder(buffers: &mut VertexBuffers<Point, u16>) -> SimpleBuffersBuilder {
440 let first_vertex = buffers.vertices.len() as Index;
441 let first_index = buffers.indices.len() as Index;
442 BuffersBuilder {
443 buffers,
444 first_vertex,
445 first_index,
446 vertex_offset: 0,
447 vertex_constructor: Positions,
448 }
449}
450
451impl<'l, OutputVertex, OutputIndex, Ctor> GeometryBuilder
452 for BuffersBuilder<'l, OutputVertex, OutputIndex, Ctor>
453where
454 OutputVertex: 'l,
455 OutputIndex: Add + From<VertexId> + MaxIndex,
456{
457 fn begin_geometry(&mut self) {
458 self.first_vertex = self.buffers.vertices.len() as Index;
459 self.first_index = self.buffers.indices.len() as Index;
460 }
461
462 fn add_triangle(&mut self, a: VertexId, b: VertexId, c: VertexId) {
463 #[cfg(feature = "std")]
464 if a == b || a == c || b == c {
465 std::println!("bad triangle {a:?} {b:?} {c:?}");
466 }
467 debug_assert!(a != b);
468 debug_assert!(a != c);
469 debug_assert!(b != c);
470 debug_assert!(a != VertexId::INVALID);
471 debug_assert!(b != VertexId::INVALID);
472 debug_assert!(c != VertexId::INVALID);
473 self.buffers.indices.push(a.into());
474 self.buffers.indices.push(b.into());
475 self.buffers.indices.push(c.into());
476 }
477
478 fn abort_geometry(&mut self) {
479 self.buffers.vertices.truncate(self.first_vertex as usize);
480 self.buffers.indices.truncate(self.first_index as usize);
481 }
482}
483
484impl<'l, OutputVertex, OutputIndex, Ctor> FillGeometryBuilder
485 for BuffersBuilder<'l, OutputVertex, OutputIndex, Ctor>
486where
487 OutputVertex: 'l,
488 OutputIndex: Add + From<VertexId> + MaxIndex,
489 Ctor: FillVertexConstructor<OutputVertex>,
490{
491 fn add_fill_vertex(&mut self, v: FillVertex) -> Result<VertexId, GeometryBuilderError> {
492 let v = self.vertex_constructor.new_vertex(v);
493 self.add_vertex_impl(v)
494 }
495}
496
497impl<'l, OutputVertex, OutputIndex, Ctor> StrokeGeometryBuilder
498 for BuffersBuilder<'l, OutputVertex, OutputIndex, Ctor>
499where
500 OutputVertex: 'l,
501 OutputIndex: Add + From<VertexId> + MaxIndex,
502 Ctor: StrokeVertexConstructor<OutputVertex>,
503{
504 fn add_stroke_vertex(&mut self, v: StrokeVertex) -> Result<VertexId, GeometryBuilderError> {
505 let v = self.vertex_constructor.new_vertex(v);
506 self.add_vertex_impl(v)
507 }
508}
509
510/// A geometry builder that does not output any geometry.
511///
512/// Mostly useful for testing.
513pub struct NoOutput {
514 next_vertex: u32,
515}
516
517impl NoOutput {
518 pub fn new() -> Self {
519 NoOutput { next_vertex: 0 }
520 }
521}
522
523impl Default for NoOutput {
524 fn default() -> Self {
525 Self::new()
526 }
527}
528
529impl GeometryBuilder for NoOutput {
530 fn add_triangle(&mut self, a: VertexId, b: VertexId, c: VertexId) {
531 debug_assert!(a != b);
532 debug_assert!(a != c);
533 debug_assert!(b != c);
534 }
535}
536
537impl FillGeometryBuilder for NoOutput {
538 fn add_fill_vertex(&mut self, _vertex: FillVertex) -> Result<VertexId, GeometryBuilderError> {
539 if self.next_vertex == u32::MAX {
540 return Err(GeometryBuilderError::TooManyVertices);
541 }
542 self.next_vertex += 1;
543 Ok(VertexId(self.next_vertex - 1))
544 }
545}
546
547impl StrokeGeometryBuilder for NoOutput {
548 fn add_stroke_vertex(&mut self, _: StrokeVertex) -> Result<VertexId, GeometryBuilderError> {
549 if self.next_vertex == u32::MAX {
550 return Err(GeometryBuilderError::TooManyVertices);
551 }
552 self.next_vertex += 1;
553 Ok(VertexId(self.next_vertex - 1))
554 }
555}
556
557/// Provides the maximum value of an index.
558///
559/// This should be the maximum value representable by the index type up
560/// to `u32::MAX - 1` because the tessellators can't internally represent more
561/// than `u32::MAX - 1` indices.
562pub trait MaxIndex {
563 const MAX: usize;
564}
565
566impl MaxIndex for u8 {
567 const MAX: usize = u8::MAX as usize;
568}
569impl MaxIndex for i8 {
570 const MAX: usize = i8::MAX as usize;
571}
572impl MaxIndex for u16 {
573 const MAX: usize = u16::MAX as usize;
574}
575impl MaxIndex for i16 {
576 const MAX: usize = i16::MAX as usize;
577}
578impl MaxIndex for u32 {
579 // u32::MAX is reserved as VertexId::INVALID
580 const MAX: usize = (u32::MAX - 1) as usize;
581}
582impl MaxIndex for i32 {
583 const MAX: usize = i32::MAX as usize;
584}
585impl MaxIndex for u64 {
586 // The tessellators internally use u32 indices so we can't have more than for u32
587 const MAX: usize = <u32 as MaxIndex>::MAX;
588}
589impl MaxIndex for i64 {
590 const MAX: usize = <u32 as MaxIndex>::MAX;
591}
592impl MaxIndex for usize {
593 #[cfg(target_pointer_width = "64")]
594 const MAX: usize = <u64 as MaxIndex>::MAX;
595 #[cfg(target_pointer_width = "32")]
596 const MAX: usize = <u32 as MaxIndex>::MAX;
597}
598impl MaxIndex for isize {
599 #[cfg(target_pointer_width = "64")]
600 const MAX: usize = <i64 as MaxIndex>::MAX;
601 #[cfg(target_pointer_width = "32")]
602 const MAX: usize = <i32 as MaxIndex>::MAX;
603}
604
605#[cfg(test)]
606mod tests {
607 use super::*;
608
609 #[test]
610 fn overflow_with_vertex_offset() {
611 let mut buffers = VertexBuffers::default();
612 let mut builder = simple_builder(&mut buffers).with_vertex_offset(65534);
613 let builder = &mut builder as &mut dyn FillGeometryBuilder;
614
615 let dummy_queue = crate::event_queue::EventQueue::new();
616 let vertex = || crate::fill::FillVertex {
617 position: Point::new(0.0, 0.0),
618 events: &dummy_queue,
619 current_event: crate::event_queue::INVALID_EVENT_ID,
620 attrib_buffer: &mut [],
621 attrib_store: None,
622 };
623
624 builder.begin_geometry();
625 assert_eq!(builder.add_fill_vertex(vertex()), Ok(VertexId(65534)));
626 assert_eq!(builder.add_fill_vertex(vertex()), Ok(VertexId(65535)));
627 assert_eq!(builder.add_fill_vertex(vertex()), Err(GeometryBuilderError::TooManyVertices));
628 builder.abort_geometry();
629 assert!(buffers.vertices.is_empty() && buffers.indices.is_empty());
630 }
631}