star-geometry-2d

Cleaning and decorating 2D geometries
git clone git://git.meso-star.fr/star-geometry-2d.git
Log | Files | Refs | README | LICENSE

sg2d.h (16899B)


      1 /* Copyright (C) 2019, 2020, 2023 |Méso|Star> (contact@meso-star.com)
      2  *
      3  * This program is free software: you can redistribute it and/or modify
      4  * it under the terms of the GNU General Public License as published by
      5  * the Free Software Foundation, either version 3 of the License, or
      6  * (at your option) any later version.
      7  *
      8  * This program is distributed in the hope that it will be useful,
      9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     11  * GNU General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License
     14  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     15 
     16 #ifndef STAR_GEOMETRY_2D_H__
     17 #define STAR_GEOMETRY_2D_H__
     18 
     19 #include <rsys/rsys.h>
     20 #include <rsys/dynamic_array_uint.h>
     21 
     22 #include <limits.h>
     23 
     24 /* Library symbol management */
     25 #if defined(SG2D_SHARED_BUILD)
     26   #define SG2D_API extern EXPORT_SYM
     27 #elif defined(SG2D_STATIC_BUILD)
     28   #define SG2D_API extern LOCAL_SYM
     29 #else /* Use shared library */
     30   #define SG2D_API extern IMPORT_SYM
     31 #endif
     32 
     33 /* Helper macro that asserts if the invocation of the star-geometry-2d function
     34  * `Func' returns an error. One should use this macro on star-geometry-2d
     35  * function calls for which no explicit error checking is performed. */
     36 #ifndef NDEBUG
     37 #define SG2D(Func) ASSERT(sg2d_ ## Func == RES_OK)
     38 #else
     39 #define SG2D(Func) sg2d_ ## Func
     40 #endif
     41 
     42 /* Forward declaration of external opaque data types */
     43 struct logger;
     44 struct mem_allocator;
     45 
     46 /* Forward declaration of the star-geometry-2d opaque data types. These data
     47  * types are ref counted. Once created the caller implicitly owns the created
     48  * data, i.e. its reference counter is set to 1. The sg2d_<TYPE>_ref_<get|put>
     49  * functions get or release a reference on the data, i.e. they increment or
     50  * decrement the reference counter, respectively. When this counter reaches 0,
     51  * the object is silently destroyed and cannot be used anymore. */
     52 struct sg2d_device;
     53 struct sg2d_geometry;
     54 
     55 /******************************************************************************
     56  * The dimension of the geometry used in the library.
     57  *****************************************************************************/
     58 #define SG2D_GEOMETRY_DIMENSION 2
     59 
     60 /******************************************************************************
     61  * A type to list the different user properties attached to segments.
     62  *****************************************************************************/
     63 enum sg2d_property_type {
     64   SG2D_FRONT,
     65   SG2D_BACK,
     66   SG2D_INTFACE,
     67   SG2D_PROP_TYPES_COUNT__
     68 };
     69 
     70 /******************************************************************************
     71  * A type to list the different possible partitions of segments.
     72  *****************************************************************************/
     73 enum sg2d_obj_dump_content {
     74   SG2D_OBJ_DUMP_MERGE_CONFLICTS = BIT(0),
     75   SG2D_OBJ_DUMP_PROPERTY_CONFLICTS = BIT(1),
     76   SG2D_OBJ_DUMP_VALID_PRIMITIVE = BIT(2),
     77   SG2D_OBJ_DUMP_ALL =
     78      SG2D_OBJ_DUMP_MERGE_CONFLICTS
     79    | SG2D_OBJ_DUMP_PROPERTY_CONFLICTS
     80    | SG2D_OBJ_DUMP_VALID_PRIMITIVE
     81 };
     82 
     83 /******************************************************************************
     84  * A type to list the different qualifiers of C code variable output.
     85  *****************************************************************************/
     86 enum sg2d_c_dump_qualifiers {
     87   SG2D_C_DUMP_CONST = BIT(0),
     88   SG2D_C_DUMP_STATIC = BIT(1)
     89 };
     90 
     91 /******************************************************************************
     92  * The value that should be used for properties attached to segments (i.e.
     93  * media or interface) when let unspecified.
     94  * SG2D_UNSPECIFIED_PROPERTY can be used for a property that has already been
     95  * defined; in this case the previous value will remain.
     96  *****************************************************************************/
     97 #define SG2D_UNSPECIFIED_PROPERTY UINT_MAX
     98 
     99 /*****************************************************************************
    100  * A type to hold callbacks for sg2d_geometry_add.
    101  ****************************************************************************/
    102 struct sg2d_geometry_add_callbacks {
    103   /* User function that provides vertices ids for added segments */
    104   void(*get_indices)
    105     (const unsigned iseg,
    106      unsigned ids[SG2D_GEOMETRY_DIMENSION],
    107      void* context);
    108   /* User function that provides properties for added segments */
    109   void(*get_properties) /* Can be NULL <=> SG2D_UNSPECIFIED_PROPERTY used */
    110     (const unsigned iseg,
    111      /* It is OK to have side and interface properties sharing the same IDs,
    112       * i.e. side and interface IDs both starting from 0 */
    113      unsigned properties[SG2D_PROP_TYPES_COUNT__],
    114      void* context);
    115   /* User function that provides coordinates for added vertices */
    116   void(*get_position)
    117     (const unsigned ivert, double pos[SG2D_GEOMETRY_DIMENSION], void* context);
    118   /* Called if the iseg_th segment of the current sg2d_geometry_add is a new
    119    * segment (i.e. not a duplicate) so that the client app can manage its own
    120    * segment data/properties/attributes.
    121    * If return is not RES_OK, sg2d_geometry_add stops immediately and returns
    122    * whatever value add_segment returned. */
    123   res_T(*add_segment) /* Can be NULL */
    124     (const unsigned unique_id, const unsigned iseg, void* context);
    125   /* Called if the iseg_th segment of the current sg2d_geometry_add is a
    126    * duplicate of the unique_id_th unique segment so that the client app can
    127    * merge its own segment data, rule merge validity, and possibly change the
    128    * recorded properties.
    129    * The reversed_segment arg indicates if the segment vertices' order is
    130    * the same it was when the segment was first added.
    131    * The merge_conflict_status argument can be set to any value. Any non-0
    132    * value is accounted for a conflict and is kept as-is in dumps, allowing
    133    * different shades of conflict.
    134    * The segment_properties and merged_properties args contain the involved
    135    * properties. */
    136   res_T(*merge_segment) /* Can be NULL */
    137     (const unsigned unique_id,
    138      const unsigned iseg,
    139      const int reversed_segment,
    140      unsigned segment_properties[SG2D_PROP_TYPES_COUNT__],
    141      const unsigned merged_properties[SG2D_PROP_TYPES_COUNT__],
    142      void* context,
    143      int* merge_conflict_status);
    144   /* Called if the iseg_th segment is degenerated. According to the value 
    145    * of abort, sg2d_geometry_add will stop and return RES_BAD_ARG or continue
    146    * silently. */
    147   res_T(*degenerated_segment) /* Can be NULL <=> Drop segment, don't abort */
    148     (const unsigned iseg, void* context, int* abort);
    149 };
    150 #define SG2D_ADD_CALLBACKS_NULL__ { NULL, NULL, NULL, NULL, NULL, NULL }
    151 
    152 BEGIN_DECLS
    153 
    154 /******************************************************************************
    155  * A helper function on properties compatibility.
    156  *****************************************************************************/
    157 static INLINE int
    158 sg2d_compatible_property
    159   (const unsigned p1,
    160    const unsigned p2)
    161 {
    162   if(p1 == SG2D_UNSPECIFIED_PROPERTY || p2 == SG2D_UNSPECIFIED_PROPERTY)
    163     return 1;
    164   return (p1 == p2);
    165 }
    166 
    167 /******************************************************************************
    168  * star-geometry-2d device. It is an handle toward the star-geometry-2d
    169  * library. It manages the star-geometry-2d resources.
    170  *****************************************************************************/
    171 SG2D_API res_T
    172 sg2d_device_create
    173   (struct logger* logger, /* Can be NULL <=> use default logger */
    174    struct mem_allocator* allocator, /* Can be NULL <=> use default allocator */
    175    const int verbose, /* Verbosity level */
    176    struct sg2d_device** dev);
    177 
    178 SG2D_API res_T
    179 sg2d_device_ref_get
    180   (struct sg2d_device* dev);
    181 
    182 SG2D_API res_T
    183 sg2d_device_ref_put
    184   (struct sg2d_device* dev);
    185 
    186 /******************************************************************************
    187  * star-geometry-2d geometry.
    188  * It stores decorated geometry accumulated through calls to sg2d_geometry_add,
    189  * information related to this geometry and its creation process, including
    190  * merge conflicts.
    191  *****************************************************************************/
    192 /* Create a geometry that can be used to accumulate vertices and segments
    193  * decorated with properties. */
    194 SG2D_API res_T
    195 sg2d_geometry_create
    196   (struct sg2d_device* dev,
    197    struct sg2d_geometry** geometry);
    198 
    199 /* Reserve memory according to anticipated geometry size. */
    200 SG2D_API res_T
    201 sg2d_geometry_reserve
    202   (struct sg2d_geometry* geometry,
    203    const unsigned vertices_count,
    204    const unsigned segments_count,
    205    const unsigned properties_count);
    206 
    207 /* Add a new set of 2D vertices and segments to the geometry; segments can
    208  * be decorated with 3 properties (front and back media and interface) that can
    209  * be let unspecified using the SG2D_UNSPECIFIED_PROPERTY special value.
    210  * Vertices can be duplicates and are silently deduplicated, always valid.
    211  * Segments can be duplicates, but this can be ruled invalid due to property
    212  * inconsistency. Segment duplicates are silently deduplicated, even if
    213  * invalid. Consistency is to be understood as the consistency of the
    214  * successive values for the same property across calls of sg2d_geometry_add,
    215  * not as the consistency of the values of the 3 properties of a segment at
    216  * some given time (this question has its own callback (validate) in the
    217  * sg2d_geometry_validate_properties API call). 
    218  * Duplicate segments validity is either ruled by the user-provided
    219  * merge_segment callback, or is just a matter of properties consistency if no
    220  * callback is provided. In either case, sg2d_geometry_add never stops
    221  * prematurely nor returns an error code due to a merge conflict.
    222  * - if provided, the callback must return the consistency status using the
    223  *   merge_conflict_status int* paramater (0 for consistent; any other value
    224  *   for inconsistent, this value being recorded); regardless of
    225  *   merge_conflict_status, the callback can change the properties of the
    226  *   segment before the SG2D_UNSPECIFIED_PROPERTY overwriting step;
    227  * - if not, a non-SG2D_UNSPECIFIED_PROPERTY is only consistent with itself and
    228  *   with SG2D_UNSPECIFIED_PROPERTY (if inconsistent, merge_conflict_status is
    229  *   set to 1 and recorded) ; regardless of merge_conflict_status, a
    230  *   non-SG2D_UNSPECIFIED_PROPERTY property is never overridden.
    231  * When deduplicating segments, the first occurence remains (with its
    232  * original index in user world). After consistency being computed, a final
    233  * step consists in rewriting SG2D_UNSPECIFIED_PROPERTY properties if the
    234  * merged property is defined. */
    235 SG2D_API res_T
    236 sg2d_geometry_add
    237   (struct sg2d_geometry* geometry,
    238    const unsigned vertices_count,
    239    const unsigned segments_count,
    240    const struct sg2d_geometry_add_callbacks* callbacks,
    241    void* context); /* Can be NULL */
    242 
    243 /* Apply a validation callback on each segment included in this geometry that
    244  * is not already flagged with a merge error. If validate returns a non-RES_OK
    245  * value, the validation stops and returns the same error value; on the other
    246  * hand, validation goes to the end regardless of properties conflicts.
    247  * The properties_conflict_status argument can be set to any value. Any non-0
    248  * value is accounted for a conflict and is kept as-is in dumps, allowing
    249  * different shades of conflict.
    250  * If validation is processed again, the properties conflict count is reset,
    251  * as well as the properties_conflict_status status of the segments. */
    252 SG2D_API res_T
    253 sg2d_geometry_validate_properties
    254   (struct sg2d_geometry* geometry,
    255    res_T(*validate)
    256      (const unsigned iseg,
    257       const unsigned properties[SG2D_PROP_TYPES_COUNT__],
    258       void* context,
    259       int* properties_conflict_status),
    260    void* context); /* Can be NULL */
    261 
    262 /* Get the number of unique vertices. */
    263 SG2D_API res_T
    264 sg2d_geometry_get_unique_vertices_count
    265   (const struct sg2d_geometry* geometry,
    266    unsigned* count);
    267 
    268 /* Get the ivtx_th vertex. */
    269 SG2D_API res_T
    270 sg2d_geometry_get_unique_vertex
    271   (const struct sg2d_geometry* geometry,
    272    const unsigned ivtx,
    273    double coord[SG2D_GEOMETRY_DIMENSION]);
    274 
    275 /* Get the number of segments added to the geometry, regardless of unicity. */
    276 SG2D_API res_T
    277 sg2d_geometry_get_added_segments_count
    278   (const struct sg2d_geometry* geometry,
    279    unsigned* count);
    280 
    281 /* Get the number of unique segments. */
    282 SG2D_API res_T
    283 sg2d_geometry_get_unique_segments_count
    284   (const struct sg2d_geometry* geometry,
    285    unsigned* count);
    286 
    287 /* Get the vertex indices of the iseg_th unique segment. */
    288 SG2D_API res_T
    289 sg2d_geometry_get_unique_segment_vertices
    290   (const struct sg2d_geometry* geometry,
    291    const unsigned iseg,
    292    unsigned indices[SG2D_GEOMETRY_DIMENSION]);
    293 
    294 /* Get the properties of the iseg_th unique segment. */
    295 SG2D_API res_T
    296 sg2d_geometry_get_unique_segment_properties
    297   (const struct sg2d_geometry* geometry,
    298    const unsigned iseg,
    299    unsigned properties[SG2D_PROP_TYPES_COUNT__]);
    300 
    301 /* Get the user ID of the iseg_th unique segment, that is the user world's
    302  * index of the segment that first created this unique segment.
    303  * User world index starts at 0 and increases for every segment that is
    304  * submitted to sg2d_geometry_add calls, regardless of duplication or
    305  * sg2d_geometry_add failures (non-RES_OK return value). */
    306 SG2D_API res_T
    307 sg2d_geometry_get_unique_segment_user_id
    308   (const struct sg2d_geometry* geometry,
    309    const unsigned iseg,
    310    unsigned* user_id);
    311 
    312 /* Get the number of segments with (at least) one unspecified side. */
    313 SG2D_API res_T
    314 sg2d_geometry_get_unique_segments_with_unspecified_side_count
    315   (const struct sg2d_geometry* geometry,
    316    unsigned* count);
    317 
    318 /* Get the number of segments with unspecified interface. */
    319 SG2D_API res_T
    320 sg2d_geometry_get_unique_segments_with_unspecified_interface_count
    321   (const struct sg2d_geometry* geometry,
    322    unsigned* count);
    323 
    324 /* Get the number of segments flagged with a merge conflict. */
    325 SG2D_API res_T
    326 sg2d_geometry_get_unique_segments_with_merge_conflict_count
    327   (const struct sg2d_geometry* geometry,
    328    unsigned* count);
    329 
    330 /* Get the number of segments flagged with a property conflict. Only meaningful
    331  * after sg2d_geometry_validate_properties has been called. */
    332 SG2D_API res_T
    333 sg2d_geometry_get_unique_segments_with_properties_conflict_count
    334   (const struct sg2d_geometry* geometry,
    335    unsigned* count);
    336 
    337 /* Dump a geometry in the provided stream in the OBJ format.
    338  * The geometry can include conflicts, but cannot be empty. Note that vertices
    339  * in this output are 3D with Z==0, as OBJ files cannot define 2D vertices.
    340  * The dump is made of the vertices and some segments, without their
    341  * properties. The dumped segments are defined by the flags argument, that
    342  * must be ORed enum sg2d_obj_dump_content values.
    343  * The dumped segments are partitioned in the following groups:
    344  * - Valid_segments
    345  * - Merge_conflicts
    346  * - Property_conflict */
    347 SG2D_API res_T
    348 sg2d_geometry_dump_as_obj
    349   (const struct sg2d_geometry* geometry,
    350    FILE* stream,
    351    const int flags);
    352 
    353 /* Dump a geometry in the provided stream in the VTK ascii format.
    354  * The geometry can include conflicts, but cannot be empty. Note that POINTS
    355  * in this output are 3D with Z==0, as VTK text files cannot define 2D POINTS.
    356  * The dump is made of the vertices and segments, with most of their
    357  * properties:
    358  * - Front_medium (medium ID of the front side, INT_MAX for both unspecified
    359  *   and conflicted)
    360  * - Back_medium (medium ID of the back side, INT_MAX for both unspecified and
    361  *   conflicted)
    362  * - Interface (interface ID, INT_MAX for both unspecified and conflicted)
    363  * - Merge_conflict (merge conflict status)
    364  * - Property_conflict (property conflict status)
    365  * - Created_at_sg2d_geometry_add (rank of the sg2d_geometry_add that created
    366  *   the segment) */
    367 SG2D_API res_T
    368 sg2d_geometry_dump_as_vtk
    369   (const struct sg2d_geometry* geometry,
    370    FILE* stream);
    371 
    372 /* Dump the geometry as C code.
    373  * The geometry cannot be empty and must be conflict-free.
    374  * The C code defines the 5 variables:
    375  * - [static] [const] unsigned <name_prefix>_vertices_count = N1;
    376  * - [static] [const] double <name_prefix>_vertices[<N1>] = { .... };
    377  * - [static] [const] unsigned <name_prefix>_segments_count = N2;
    378  * - [static] [const] unsigned <name_prefix>_segments[<N2>] = { .... };
    379  * - [static] [const] unsigned <name_prefix>_properties[<N3>] = { .... };
    380  * Where <N1> is 2 * vertices_count, <N2> is 2 * segments_count and <N3> is
    381  * 3 * segments_count.
    382  * The two qualifiers static and const are output or not according to flags;
    383  * flags must be ORed enum sg2d_c_dump_qualifiers values. */
    384 SG2D_API res_T
    385 sg2d_geometry_dump_as_c_code
    386   (const struct sg2d_geometry* geometry,
    387    FILE* stream,
    388    const char* name_prefix, /* Can be NULL or "" */
    389    const int flags);
    390 
    391 SG2D_API res_T
    392 sg2d_geometry_ref_get
    393   (struct sg2d_geometry* geometry);
    394 
    395 SG2D_API res_T
    396 sg2d_geometry_ref_put
    397   (struct sg2d_geometry* geometry);
    398 
    399 END_DECLS
    400 
    401 #endif /* STAR_GEOMETRY_2D_H__ */