Skip to main content

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}