PropertyManager.hh 35.7 KB
Newer Older
Jan Möbius's avatar
Jan Möbius committed
1
/* ========================================================================= *
2
3
 *                                                                           *
 *                               OpenMesh                                    *
Jan Möbius's avatar
Jan Möbius committed
4
 *           Copyright (c) 2001-2015, RWTH-Aachen University                 *
Jan Möbius's avatar
Typo    
Jan Möbius committed
5
 *           Department of Computer Graphics and Multimedia                  *
Jan Möbius's avatar
Jan Möbius committed
6
7
 *                          All rights reserved.                             *
 *                            www.openmesh.org                               *
8
9
 *                                                                           *
 *---------------------------------------------------------------------------*
Jan Möbius's avatar
Jan Möbius committed
10
11
 * This file is part of OpenMesh.                                            *
 *---------------------------------------------------------------------------*
12
 *                                                                           *
Jan Möbius's avatar
Jan Möbius committed
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 * Redistribution and use in source and binary forms, with or without        *
 * modification, are permitted provided that the following conditions        *
 * are met:                                                                  *
 *                                                                           *
 * 1. Redistributions of source code must retain the above copyright notice, *
 *    this list of conditions and the following disclaimer.                  *
 *                                                                           *
 * 2. Redistributions in binary form must reproduce the above copyright      *
 *    notice, this list of conditions and the following disclaimer in the    *
 *    documentation and/or other materials provided with the distribution.   *
 *                                                                           *
 * 3. Neither the name of the copyright holder nor the names of its          *
 *    contributors may be used to endorse or promote products derived from   *
 *    this software without specific prior written permission.               *
 *                                                                           *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS       *
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A           *
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,  *
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,       *
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR        *
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF    *
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING      *
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS        *
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.              *
Jan Möbius's avatar
Jan Möbius committed
39
40
 *                                                                           *
 * ========================================================================= */
41
42
43
44

#ifndef PROPERTYMANAGER_HH_
#define PROPERTYMANAGER_HH_

45
46
#include <OpenMesh/Core/System/config.h>
#include <OpenMesh/Core/Utils/HandleToPropHandle.hh>
47
#include <OpenMesh/Core/Mesh/PolyConnectivity.hh>
48
#include <sstream>
49
#include <stdexcept>
50
#include <string>
51
52
53
54
55
56
57
58

namespace OpenMesh {

/**
 * This class is intended to manage the lifecycle of properties.
 * It also defines convenience operators to access the encapsulated
 * property's value.
 *
59
60
61
 * It is recommended to use the factory functions
 * makeTemporaryProperty(), getProperty(), and getOrMakeProperty()
 * to construct a PropertyManager, e.g.
62
 *
63
64
 * Note that the second template parameter is depcretated.
 *
65
 * \code
66
67
68
 * {
 *     TriMesh mesh;
 *     auto visited = makeTemporaryProperty<VertexHandle, bool>(mesh);
69
 *
70
71
72
73
 *     for (auto vh : mesh.vertices()) {
 *         if (!visited[vh]) {
 *             visitComponent(mesh, vh, visited);
 *         }
74
 *     }
75
 *     // The property is automatically removed at the end of the scope
76
77
 * }
 * \endcode
78
 */
79
template<typename PROPTYPE, typename MeshT = int>
80
81
class PropertyManager {

82
    public:
Max Lyon's avatar
Max Lyon committed
83
84
85
        using Value      = typename PROPTYPE::Value;
        using value_type = typename PROPTYPE::value_type;
        using Handle     = typename PROPTYPE::Handle;
86
87
        using Self       = PropertyManager<PROPTYPE, MeshT>;

88
    private:
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
        // Mesh properties (MPropHandleT<...>) are stored differently than the other properties.
        // This class implements different behavior when copying or swapping data from one
        // property manager to a another one.
        template <typename PropertyManager2, typename PropHandleT>
        struct StorageT;

        // specialization for Mesh Properties
        template <typename PropertyManager2>
        struct StorageT<PropertyManager2, MPropHandleT<Value>> {
          static void copy(const PropertyManager<PROPTYPE, MeshT>& from, PropertyManager2& to) {
            *to = *from;
          }
          static void swap(PropertyManager<PROPTYPE, MeshT>& from, PropertyManager2& to) {
            std::swap(*to, *from);
          }
104
105
106
107
108
109
          static const Value& access_property_const(PolyConnectivity& mesh, const PROPTYPE& prop_handle, const Handle&) {
            return mesh.property(prop_handle);
          }
          static Value& access_property(PolyConnectivity& mesh, const PROPTYPE& prop_handle, const Handle&) {
            return mesh.property(prop_handle);
          }
110
        };
111

112
113
114
115
116
117
118
        // definition for other Mesh Properties
        template <typename PropertyManager2, typename PropHandleT>
        struct StorageT {
          static void copy(const PropertyManager<PROPTYPE, MeshT>& from, PropertyManager2& to) {
            from.copy_to(from.mesh_.template all_elements<Handle>(), to, to.mesh_.template all_elements<Handle>());
          }
          static void swap(PropertyManager<PROPTYPE, MeshT>& lhs, PropertyManager2& rhs) {
119
            std::swap(lhs.mesh().property(lhs.prop_).data_vector(), rhs.mesh().property(rhs.prop_).data_vector());
120
            // resize the property to the correct size
121
122
123
124
125
126
127
128
            lhs.mesh().property(lhs.prop_).resize(lhs.mesh().template n_elements<Handle>());
            rhs.mesh().property(rhs.prop_).resize(rhs.mesh().template n_elements<Handle>());
          }
          static const Value& access_property_const(PolyConnectivity& mesh, const PROPTYPE& prop_handle, const Handle& handle) {
            return mesh.property(prop_handle, handle);
          }
          static Value& access_property(PolyConnectivity& mesh, const PROPTYPE& prop_handle, const Handle& handle) {
            return mesh.property(prop_handle, handle);
129
130
131
132
          }
        };

        using Storage = StorageT<Self, PROPTYPE>;
133
134

    public:
Max Lyon's avatar
Max Lyon committed
135

136
        /**
Max Lyon's avatar
Max Lyon committed
137
138
         * @deprecated Use a constructor without \p existing and check existance with hasProperty() instead.
         *
139
140
141
142
143
144
145
146
147
148
149
150
         * Constructor.
         *
         * Throws an \p std::runtime_error if \p existing is true and
         * no property named \p propname of the appropriate property type
         * exists.
         *
         * @param mesh The mesh on which to create the property.
         * @param propname The name of the property.
         * @param existing If false, a new property is created and its lifecycle is managed (i.e.
         * the property is deleted upon destruction of the PropertyManager instance). If true,
         * the instance merely acts as a convenience wrapper around an existing property with no
         * lifecycle management whatsoever.
151
         *
Max Lyon's avatar
Max Lyon committed
152
153
         * @see PropertyManager::getOrMakeProperty, PropertyManager::getProperty,
         * PropertyManager::makeTemporaryProperty
154
         */
155
        OM_DEPRECATED("Use the constructor without parameter 'existing' instead. Check for existance with hasProperty") // As long as this overload exists, initial value must be first parameter due to ambiguity for properties of type bool
Max Lyon's avatar
Max Lyon committed
156
        PropertyManager(PolyConnectivity& mesh, const char *propname, bool existing) : mesh_(mesh), retain_(existing), name_(propname) {
157
            if (existing) {
158
                if (!PropertyManager::mesh().get_property_handle(prop_, propname)) {
159
160
161
162
163
                    std::ostringstream oss;
                    oss << "Requested property handle \"" << propname << "\" does not exist.";
                    throw std::runtime_error(oss.str());
                }
            } else {
164
                PropertyManager::mesh().add_property(prop_, propname);
165
166
167
            }
        }

Max Lyon's avatar
Max Lyon committed
168
169
170
171
172
173
174
175
176
        /**
         * Constructor.
         *
         * Asks for a property with name propname and creates one if none exists. Lifetime is not managed.
         *
         * @param mesh The mesh on which to create the property.
         * @param propname The name of the property.
         */
        PropertyManager(PolyConnectivity& mesh, const char *propname) : mesh_(mesh), retain_(true), name_(propname) {
177
178
            if (!PropertyManager::mesh().get_property_handle(prop_, propname)) {
              PropertyManager::mesh().add_property(prop_, propname);
179
180
181
            }
        }

Max Lyon's avatar
Max Lyon committed
182
183
184
185
186
        /**
         * Constructor.
         *
         * Asks for a property with name propname and creates one if none exists. Lifetime is not managed.
         *
Max Lyon's avatar
Max Lyon committed
187
         * @param initial_value If the proeprty is newly created, it will be initialized with initial_value.
188
         *        If the property already existed, nothing is changes.
Max Lyon's avatar
Max Lyon committed
189
190
191
         * @param mesh The mesh on which to create the property.
         * @param propname The name of the property.
         */
Max Lyon's avatar
Max Lyon committed
192
        PropertyManager(const Value& initial_value, PolyConnectivity& mesh, const char *propname) : mesh_(mesh), retain_(true), name_(propname) {
Max Lyon's avatar
Max Lyon committed
193
            if (!mesh_.get_property_handle(prop_, propname)) {
194
              PropertyManager::mesh().add_property(prop_, propname);
Max Lyon's avatar
Max Lyon committed
195
              set_range(mesh_.all_elements<Handle>(), initial_value);
Max Lyon's avatar
Max Lyon committed
196
197
198
199
200
201
202
203
204
205
            }
        }

        /**
         * Constructor.
         *
         * Create an anonymous property. Lifetime is managed.
         *
         * @param mesh The mesh on which to create the property.
         */
206
207
        PropertyManager(const PolyConnectivity& mesh) : mesh_(mesh), retain_(false), name_("") {
            PropertyManager::mesh().add_property(prop_, name_);
Max Lyon's avatar
Max Lyon committed
208
209
210
211
212
213
214
        }

        /**
         * Constructor.
         *
         * Create an anonymous property. Lifetime is managed.
         *
Max Lyon's avatar
Max Lyon committed
215
         * @param initial_value The property will be initialized with initial_value.
Max Lyon's avatar
Max Lyon committed
216
217
         * @param mesh The mesh on which to create the property.
         */
218
        PropertyManager(const Value& initial_value, const PolyConnectivity& mesh) : mesh_(mesh), retain_(false), name_("") {
219
            PropertyManager::mesh().add_property(prop_, name_);
Max Lyon's avatar
Max Lyon committed
220
            set_range(mesh_.all_elements<Handle>(), initial_value);
Max Lyon's avatar
Max Lyon committed
221
222
223
224
225
226
227
228
        }

        /**
         * Constructor.
         *
         * Create a wrapper around an existing property. Lifetime is not managed.
         *
         * @param mesh The mesh on which to create the property.
Max Lyon's avatar
Max Lyon committed
229
         * @param property_handle Handle to an existing property that should be wrapped.
Max Lyon's avatar
Max Lyon committed
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
         */
        PropertyManager(PolyConnectivity& mesh, PROPTYPE property_handle) : mesh_(mesh), prop_(property_handle), retain_(true), name_() {
        }

        PropertyManager() = delete;

        PropertyManager(const PropertyManager& rhs)
          :
            mesh_(rhs.mesh_),
            prop_(),
            retain_(rhs.retain_),
            name_(rhs.name_)
        {
          if (rhs.retain_) // named property -> create a property manager referring to the same
          {
            prop_ = rhs.prop_;
          }
          else // unnamed property -> create a property manager refering to a new property and copy the contents
          {
249
            PropertyManager::mesh().add_property(prop_, name_);
250
            Storage::copy(rhs, *this);
Max Lyon's avatar
Max Lyon committed
251
252
253
254
255
256
257
258
          }
        }

        PropertyManager& operator=(const PropertyManager& rhs)
        {
          if (&mesh_ == &rhs.mesh_ && prop_ == rhs.prop_)
            ; // nothing to do
          else
259
            Storage::copy(rhs, *this);
Max Lyon's avatar
Max Lyon committed
260
          return *this;
261
262
        }

263
264
265
266
        ~PropertyManager() {
            deleteProperty();
        }

267
        void swap(PropertyManager &rhs) {
Max Lyon's avatar
Max Lyon committed
268
269
          // swap the data stored in the properties
          Storage::swap(rhs, *this);
270
271
        }

272
        static bool propertyExists(PolyConnectivity &mesh, const char *propname) {
273
274
275
276
            PROPTYPE dummy;
            return mesh.get_property_handle(dummy, propname);
        }

277
        bool isValid() const { return prop_.is_valid(); }
278
279
280
281
        operator bool() const { return isValid(); }

        const PROPTYPE &getRawProperty() const { return prop_; }

282
283
        const std::string &getName() const { return name_; }

284
285
286
287
288
289
290
291
292
293
294
295
296
297
        /**
         * Get the mesh corresponding to the property.
         *
         * If you use PropertyManager without second template parameter (recommended)
         * you need to specify the actual mesh type when using this function, e.g.:
         * \code
         * {
         *     TriMesh mesh;
         *     auto visited = VProp<bool>(mesh);
         *     TriMesh& mesh_ref = visited.getMesh<TriMesh>();
         * }
         *
         */
        template <typename MeshType >
298
        const MeshType& getMesh() const { return dynamic_cast<MeshType&>(mesh_); }
299

300
        const MeshT& getMesh() const { return dynamic_cast<MeshT&>(mesh_); }
301

302
303
304
        /**
         * Move constructor. Transfers ownership (delete responsibility).
         */
Max Lyon's avatar
Max Lyon committed
305
306
307
308
309
310
311
312
313
        PropertyManager(PropertyManager &&rhs)
          :
            mesh_(rhs.mesh_),
            prop_(rhs.prop_),
            retain_(rhs.retain_),
            name_(rhs.name_)
        {
          if (!rhs.retain_)
            rhs.prop_.invalidate(); // only invalidate unnamed properties
314
315
316
317
318
        }

        /**
         * Move assignment. Transfers ownership (delete responsibility).
         */
Max Lyon's avatar
Max Lyon committed
319
320
321
322
323
324
325
        PropertyManager& operator=(PropertyManager&& rhs)
        {
            if ((&mesh_ != &rhs.mesh_) || (prop_ != rhs.prop_))
            {
              if (rhs.retain_)
              {
                // retained properties cannot be invalidated. Copy instead
326
                Storage::copy(rhs, *this);
Max Lyon's avatar
Max Lyon committed
327
328
329
              }
              else
              {
Max Lyon's avatar
Max Lyon committed
330
                // swap the data stored in the properties
331
                Storage::swap(rhs, *this);
Max Lyon's avatar
Max Lyon committed
332
                // remove the property from rhs
333
                rhs.mesh().remove_property(rhs.prop_);
Max Lyon's avatar
Max Lyon committed
334
335
336
                // invalidate prop_
                rhs.prop_.invalidate();
              }
337
            }
338
339
            return *this;
        }
340
341
342
343
344

        /**
         * Create a property manager for the supplied property and mesh.
         * If the property doesn't exist, it is created. In any case,
         * lifecycle management is disabled.
345
346
         *
         * @see makePropertyManagerFromExistingOrNew
347
         */
348
        static PropertyManager createIfNotExists(PolyConnectivity &mesh, const char *propname) {
Max Lyon's avatar
Max Lyon committed
349
            return PropertyManager(mesh, propname);
350
        }
351

352
353
354
355
356
357
358
359
360
361
        /**
         * Like createIfNotExists() with two parameters except, if the property
         * doesn't exist, it is initialized with the supplied value over
         * the supplied range after creation. If the property already exists,
         * this method has the exact same effect as the two parameter version.
         * Lifecycle management is disabled in any case.
         *
         * @see makePropertyManagerFromExistingOrNew
         */
        template<typename PROP_VALUE, typename ITERATOR_TYPE>
362
        static PropertyManager createIfNotExists(PolyConnectivity &mesh, const char *propname,
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
                const ITERATOR_TYPE &begin, const ITERATOR_TYPE &end,
                const PROP_VALUE &init_value) {
            const bool exists = propertyExists(mesh, propname);
            PropertyManager pm(mesh, propname, exists);
            pm.retain();
            if (!exists)
                pm.set_range(begin, end, init_value);
            return std::move(pm);
        }

        /**
         * Like createIfNotExists() with two parameters except, if the property
         * doesn't exist, it is initialized with the supplied value over
         * the supplied range after creation. If the property already exists,
         * this method has the exact same effect as the two parameter version.
         * Lifecycle management is disabled in any case.
         *
         * @see makePropertyManagerFromExistingOrNew
         */
        template<typename PROP_VALUE, typename ITERATOR_RANGE>
383
        static PropertyManager createIfNotExists(PolyConnectivity &mesh, const char *propname,
384
385
386
387
                const ITERATOR_RANGE &range, const PROP_VALUE &init_value) {
            return createIfNotExists(
                    mesh, propname, range.begin(), range.end(), init_value);
        }
388

389

390
        /**
391
392
393
394
395
396
397
398
399
400
         * Access the value of the encapsulated mesh property.
         *
         * Example:
         * @code
         * PolyMesh m;
         * auto description = getOrMakeProperty<void, std::string>(m, "description");
         * *description = "This is a very nice mesh.";
         * @endcode
         *
         * @note This method is only used for mesh properties.
401
         */
402
        typename PROPTYPE::reference& operator*() {
403
            return mesh().mproperty(prop_)[0];
404
405
406
        }

        /**
407
408
409
410
411
412
413
414
415
416
         * Access the value of the encapsulated mesh property.
         *
         * Example:
         * @code
         * PolyMesh m;
         * auto description = getProperty<void, std::string>(m, "description");
         * std::cout << *description << std::endl;
         * @endcode
         *
         * @note This method is only used for mesh properties.
417
         */
418
        typename PROPTYPE::const_reference& operator*() const {
419
            return mesh().mproperty(prop_)[0];
420
421
422
423
424
425
426
427
428
        }

        /**
         * Enables convenient access to the encapsulated property.
         *
         * For a usage example see this class' documentation.
         *
         * @param handle A handle of the appropriate handle type. (I.e. \p VertexHandle for \p VPropHandleT, etc.)
         */
429
430
        inline typename PROPTYPE::reference operator[] (Handle handle) {
            return mesh().property(prop_, handle);
431
432
433
434
435
436
437
438
439
        }

        /**
         * Enables convenient access to the encapsulated property.
         *
         * For a usage example see this class' documentation.
         *
         * @param handle A handle of the appropriate handle type. (I.e. \p VertexHandle for \p VPropHandleT, etc.)
         */
440
441
        inline typename PROPTYPE::const_reference operator[] (const Handle& handle) const {
            return mesh().property(prop_, handle);
442
443
        }

444
445
446
447
448
449
450
        /**
         * Enables convenient access to the encapsulated property.
         *
         * For a usage example see this class' documentation.
         *
         * @param handle A handle of the appropriate handle type. (I.e. \p VertexHandle for \p VPropHandleT, etc.)
         */
451
452
453
        inline typename PROPTYPE::reference operator() (const Handle& handle = Handle()) {
//            return mesh().property(prop_, handle);
            return Storage::access_property(mesh(), prop_, handle);
454
455
456
457
458
459
460
461
462
        }

        /**
         * Enables convenient access to the encapsulated property.
         *
         * For a usage example see this class' documentation.
         *
         * @param handle A handle of the appropriate handle type. (I.e. \p VertexHandle for \p VPropHandleT, etc.)
         */
463
464
465
        inline typename PROPTYPE::const_reference operator() (const Handle& handle = Handle()) const {
//            return mesh().property(prop_, handle);
            return Storage::access_property_const(mesh(), prop_, handle);
466
467
        }

468
469
470
471
472
473
        /**
         * Conveniently set the property for an entire range of values.
         *
         * Examples:
         * \code
         * MeshT mesh;
474
         * PropertyManager<VPropHandleT<double>> distance(
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
         *     mesh, "distance.plugin-example.i8.informatik.rwth-aachen.de");
         * distance.set_range(
         *     mesh.vertices_begin(), mesh.vertices_end(),
         *     std::numeric_limits<double>::infinity());
         * \endcode
         * or
         * \code
         * MeshT::VertexHandle vh;
         * distance.set_range(
         *     mesh.vv_begin(vh), mesh.vv_end(vh),
         *     std::numeric_limits<double>::infinity());
         * \endcode
         *
         * @param begin Start iterator. Needs to dereference to HandleType.
         * @param end End iterator. (Exclusive.)
         * @param value The value the range will be set to.
         */
492
        template<typename HandleTypeIterator, typename PROP_VALUE>
493
        void set_range(HandleTypeIterator begin, HandleTypeIterator end,
494
                const PROP_VALUE &value) {
495
496
497
498
            for (; begin != end; ++begin)
                (*this)[*begin] = value;
        }

499
#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) || __cplusplus > 199711L || defined(__GXX_EXPERIMENTAL_CXX0X__)
500
        template<typename HandleTypeIteratorRange, typename PROP_VALUE>
501
        void set_range(const HandleTypeIteratorRange &range,
502
503
504
505
506
                const PROP_VALUE &value) {
            set_range(range.begin(), range.end(), value);
        }
#endif

507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
        /**
         * Conveniently transfer the values managed by one property manager
         * onto the values managed by a different property manager.
         *
         * @param begin Start iterator. Needs to dereference to HandleType. Will
         * be used with this property manager.
         * @param end End iterator. (Exclusive.) Will be used with this property
         * manager.
         * @param dst_propmanager The destination property manager.
         * @param dst_begin Start iterator. Needs to dereference to the
         * HandleType of dst_propmanager. Will be used with dst_propmanager.
         * @param dst_end End iterator. (Exclusive.)
         * Will be used with dst_propmanager. Used to double check the bounds.
         */
        template<typename HandleTypeIterator, typename PROPTYPE_2,
522
                 typename HandleTypeIterator_2>
523
        void copy_to(HandleTypeIterator begin, HandleTypeIterator end,
524
                PropertyManager<PROPTYPE_2> &dst_propmanager,
525
526
527
                HandleTypeIterator_2 dst_begin, HandleTypeIterator_2 dst_end) const {

            for (; begin != end && dst_begin != dst_end; ++begin, ++dst_begin) {
528
                dst_propmanager[*dst_begin] = (*this)[*begin];
529
530
531
532
            }
        }

        template<typename RangeType, typename PROPTYPE_2,
533
                 typename RangeType_2>
534
        void copy_to(const RangeType &range,
535
                PropertyManager<PROPTYPE_2> &dst_propmanager,
536
                const RangeType_2 &dst_range) const {
537
538
539
540
            copy_to(range.begin(), range.end(), dst_propmanager,
                    dst_range.begin(), dst_range.end());
        }

541

542
543
544
545
546
547
548
549
550
551
552
553
554
555
        /**
         * Copy the values of a property from a source range to
         * a target range. The source range must not be smaller than the
         * target range.
         *
         * @param prop_name Name of the property to copy. Must exist on the
         * source mesh. Will be created on the target mesh if it doesn't exist.
         *
         * @param src_mesh Source mesh from which to copy.
         * @param src_range Source range which to copy. Must not be smaller than
         * dst_range.
         * @param dst_mesh Destination mesh on which to copy.
         * @param dst_range Destination range.
         */
556
        template<typename RangeType, typename RangeType_2>
557
        static void copy(const char *prop_name,
558
559
                PolyConnectivity &src_mesh, const RangeType &src_range,
                PolyConnectivity &dst_mesh, const RangeType_2 &dst_range) {
560

561
            typedef OpenMesh::PropertyManager<PROPTYPE> DstPM;
562
563
            DstPM dst(DstPM::createIfNotExists(dst_mesh, prop_name));

564
            typedef OpenMesh::PropertyManager<PROPTYPE> SrcPM;
565
566
567
568
569
            SrcPM src(src_mesh, prop_name, true);

            src.copy_to(src_range, dst, dst_range);
        }

570
571
    private:
        void deleteProperty() {
Max Lyon's avatar
Max Lyon committed
572
            if (!retain_ && prop_.is_valid())
573
574
575
576
577
578
                mesh().remove_property(prop_);
        }

        PolyConnectivity& mesh() const
        {
          return const_cast<PolyConnectivity&>(mesh_);
579
580
581
        }

    private:
582
        const PolyConnectivity& mesh_;
583
584
        PROPTYPE prop_;
        bool retain_;
585
        std::string name_;
586
587
};

588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
template <typename PropertyT>
class ConstPropertyViewer
{
public:
  using Value      = typename PropertyT::Value;
  using value_type = typename PropertyT::value_type;
  using Handle     = typename PropertyT::Handle;

  ConstPropertyViewer(const PolyConnectivity& mesh, PropertyT property_handle)
    :
      mesh_(mesh),
      prop_(property_handle)
  {}

  inline const typename PropertyT::const_reference operator() (const Handle& handle)
  {
    return mesh_.property(prop_, handle);
  }

  inline const typename PropertyT::const_reference operator[] (const Handle& handle)
  {
    return mesh_.property(prop_, handle);
  }

private:
  const PolyConnectivity& mesh_;
  PropertyT prop_;
};

617
/** @relates PropertyManager
618
619
 *
 * @deprecated Temporary properties should not have a name.
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
 *
 * Creates a new property whose lifetime is limited to the current scope.
 *
 * Used for temporary properties. Shadows any existing properties of
 * matching name and type.
 *
 * Example:
 * @code
 * PolyMesh m;
 * {
 *     auto is_quad = makeTemporaryProperty<FaceHandle, bool>(m);
 *     for (auto& fh : m.faces()) {
 *         is_quad[fh] = (m.valence(fh) == 4);
 *     }
 *     // The property is automatically removed from the mesh at the end of the scope.
 * }
 * @endcode
 *
 * @param mesh The mesh on which the property is created
 * @param propname (optional) The name of the created property
 * @tparam ElementT Element type of the created property, e.g. VertexHandle, HalfedgeHandle, etc.
 * @tparam T Value type of the created property, e.g., \p double, \p int, etc.
 * @returns A PropertyManager handling the lifecycle of the property
 */
644
645
template<typename ElementT, typename T>
PropertyManager<typename HandleToPropHandle<ElementT, T>::type>
646
647
OM_DEPRECATED("Named temporary properties are deprecated. Either create a temporary without name or a non-temporary with name")
makeTemporaryProperty(PolyConnectivity &mesh, const char *propname) {
648
    return PropertyManager<typename HandleToPropHandle<ElementT, T>::type>(mesh, propname, false);
649
650
}

651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
/** @relates PropertyManager
 *
 * Creates a new property whose lifetime is limited to the current scope.
 *
 * Used for temporary properties. Shadows any existing properties of
 * matching name and type.
 *
 * Example:
 * @code
 * PolyMesh m;
 * {
 *     auto is_quad = makeTemporaryProperty<FaceHandle, bool>(m);
 *     for (auto& fh : m.faces()) {
 *         is_quad[fh] = (m.valence(fh) == 4);
 *     }
 *     // The property is automatically removed from the mesh at the end of the scope.
 * }
 * @endcode
 *
 * @param mesh The mesh on which the property is created
 * @tparam ElementT Element type of the created property, e.g. VertexHandle, HalfedgeHandle, etc.
 * @tparam T Value type of the created property, e.g., \p double, \p int, etc.
 * @returns A PropertyManager handling the lifecycle of the property
 */
template<typename ElementT, typename T>
PropertyManager<typename HandleToPropHandle<ElementT, T>::type>
makeTemporaryProperty(PolyConnectivity &mesh) {
    return PropertyManager<typename HandleToPropHandle<ElementT, T>::type>(mesh);
}
Max Lyon's avatar
Max Lyon committed
680
681


682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
/** @relates PropertyManager
 *
 * Tests whether a property with the given element type, value type, and name is
 * present on the given mesh.
 *
 * * Example:
 * @code
 * PolyMesh m;
 * if (hasProperty<FaceHandle, bool>(m, "is_quad")) {
 *     // We now know the property exists: getProperty won't throw.
 *     auto is_quad = getProperty<FaceHandle, bool>(m, "is_quad");
 *     // Use is_quad here.
 * }
 * @endcode
 *
 * @param mesh The mesh in question
 * @param propname The property name of the expected property
 * @tparam ElementT Element type of the expected property, e.g. VertexHandle, HalfedgeHandle, etc.
 * @tparam T Value type of the expected property, e.g., \p double, \p int, etc.
 * @tparam MeshT Type of the mesh. Can often be inferred from \p mesh
 */
template<typename ElementT, typename T>
bool
hasProperty(const PolyConnectivity &mesh, const char *propname) {
    typename HandleToPropHandle<ElementT, T>::type ph;
    return mesh.get_property_handle(ph, propname);
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
}

/** @relates PropertyManager
 *
 * Obtains a handle to a named property.
 *
 * Example:
 * @code
 * PolyMesh m;
 * {
 *     try {
 *         auto is_quad = getProperty<FaceHandle, bool>(m, "is_quad");
 *         // Use is_quad here.
 *     }
 *     catch (const std::runtime_error& e) {
 *         // There is no is_quad face property on the mesh.
 *     }
 * }
 * @endcode
 *
 * @pre Property with the name \p propname of matching type exists.
 * @throws std::runtime_error if no property with the name \p propname of
 * matching type exists.
 * @param mesh The mesh on which the property is created
 * @param propname The name of the created property
 * @tparam ElementT Element type of the created property, e.g. VertexHandle, HalfedgeHandle, etc.
 * @tparam T Value type of the created property, e.g., \p double, \p int, etc.
 * @returns A PropertyManager wrapping the property
 */
737
738
739
template<typename ElementT, typename T>
PropertyManager<typename HandleToPropHandle<ElementT, T>::type>
getProperty(PolyConnectivity &mesh, const char *propname) {
740
741
742
743
744
745
746
  if (!hasProperty<ElementT, T>(mesh, propname))
  {
    std::ostringstream oss;
    oss << "Requested property handle \"" << propname << "\" does not exist.";
    throw std::runtime_error(oss.str());
  }
  return PropertyManager<typename HandleToPropHandle<ElementT, T>::type>(mesh, propname);
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
}

/** @relates PropertyManager
 *
 * Obtains a handle to a named property if it exists or creates a new one otherwise.
 *
 * Used for creating or accessing permanent properties.
 *
 * Example:
 * @code
 * PolyMesh m;
 * {
 *     auto is_quad = getOrMakeProperty<FaceHandle, bool>(m, "is_quad");
 *     for (auto& fh : m.faces()) {
 *         is_quad[fh] = (m.valence(fh) == 4);
 *     }
 *     // The property remains on the mesh after the end of the scope.
 * }
 * {
 *     // Retrieve the property from the previous scope.
 *     auto is_quad = getOrMakeProperty<FaceHandle, bool>(m, "is_quad");
 *     // Use is_quad here.
 * }
 * @endcode
 *
 * @param mesh The mesh on which the property is created
 * @param propname The name of the created property
 * @tparam ElementT Element type of the created property, e.g. VertexHandle, HalfedgeHandle, etc.
 * @tparam T Value type of the created property, e.g., \p double, \p int, etc.
 * @returns A PropertyManager wrapping the property
 */
778
779
780
781
template<typename ElementT, typename T>
PropertyManager<typename HandleToPropHandle<ElementT, T>::type>
getOrMakeProperty(PolyConnectivity &mesh, const char *propname) {
    return PropertyManager<typename HandleToPropHandle<ElementT, T>::type>::createIfNotExists(mesh, propname);
Janis Born's avatar
Janis Born committed
782
783
}

784
785
786
/** @relates PropertyManager
 * @deprecated Use makeTemporaryProperty() instead.
 *
787
788
789
 * Creates a new property whose lifecycle is managed by the returned
 * PropertyManager.
 *
790
 * Intended for temporary properties. Shadows any existing properties of
791
792
 * matching name and type.
 */
793
template<typename PROPTYPE>
794
OM_DEPRECATED("Use makeTemporaryProperty instead.")
795
PropertyManager<PROPTYPE> makePropertyManagerFromNew(PolyConnectivity &mesh, const char *propname)
796
{
797
    return PropertyManager<PROPTYPE>(mesh, propname, false);
798
799
}

800
/** \relates PropertyManager
801
802
 * @deprecated Use getProperty() instead.
 *
803
804
805
806
807
808
809
810
811
 * Creates a non-owning wrapper for an existing mesh property (no lifecycle
 * management).
 *
 * Intended for convenient access.
 *
 * @pre Property with the name \p propname of matching type exists.
 * @throws std::runtime_error if no property with the name \p propname of
 * matching type exists.
 */
812
template<typename PROPTYPE>
813
OM_DEPRECATED("Use getProperty instead.")
814
PropertyManager<PROPTYPE> makePropertyManagerFromExisting(PolyConnectivity &mesh, const char *propname)
815
{
816
    return PropertyManager<PROPTYPE>(mesh, propname, true);
817
818
}

819
820
821
/** @relates PropertyManager
 * @deprecated Use getOrMakeProperty() instead.
 *
822
823
824
825
826
 * Creates a non-owning wrapper for a mesh property (no lifecycle management).
 * If the given property does not exist, it is created.
 *
 * Intended for creating or accessing persistent properties.
 */
827
template<typename PROPTYPE>
828
OM_DEPRECATED("Use getOrMakeProperty instead.")
829
PropertyManager<PROPTYPE> makePropertyManagerFromExistingOrNew(PolyConnectivity &mesh, const char *propname)
830
{
831
    return PropertyManager<PROPTYPE>::createIfNotExists(mesh, propname);
832
833
}

834
/** @relates PropertyManager
835
836
837
838
839
840
841
842
843
844
 * Like the two parameter version of makePropertyManagerFromExistingOrNew()
 * except it initializes the property with the specified value over the
 * specified range if it needs to be created. If the property already exists,
 * this function has the exact same effect as the two parameter version.
 *
 * Creates a non-owning wrapper for a mesh property (no lifecycle management).
 * If the given property does not exist, it is created.
 *
 * Intended for creating or accessing persistent properties.
 */
Max Lyon's avatar
Max Lyon committed
845
template<typename PROPTYPE,
846
    typename ITERATOR_TYPE, typename PROP_VALUE>
847
OM_DEPRECATED("Use getOrMakeProperty instead.")
Max Lyon's avatar
Max Lyon committed
848
849
PropertyManager<PROPTYPE> makePropertyManagerFromExistingOrNew(
        PolyConnectivity &mesh, const char *propname,
850
851
        const ITERATOR_TYPE &begin, const ITERATOR_TYPE &end,
        const PROP_VALUE &init_value) {
Max Lyon's avatar
Max Lyon committed
852
    return PropertyManager<PROPTYPE>::createIfNotExists(
853
854
855
            mesh, propname, begin, end, init_value);
}

856
/** @relates PropertyManager
857
858
859
860
861
862
863
864
865
866
 * Like the two parameter version of makePropertyManagerFromExistingOrNew()
 * except it initializes the property with the specified value over the
 * specified range if it needs to be created. If the property already exists,
 * this function has the exact same effect as the two parameter version.
 *
 * Creates a non-owning wrapper for a mesh property (no lifecycle management).
 * If the given property does not exist, it is created.
 *
 * Intended for creating or accessing persistent properties.
 */
867
template<typename PROPTYPE,
868
    typename ITERATOR_RANGE, typename PROP_VALUE>
869
OM_DEPRECATED("Use getOrMakeProperty instead.")
870
871
PropertyManager<PROPTYPE> makePropertyManagerFromExistingOrNew(
        PolyConnectivity &mesh, const char *propname,
872
873
        const ITERATOR_RANGE &range,
        const PROP_VALUE &init_value) {
874
    return makePropertyManagerFromExistingOrNew<PROPTYPE>(
875
876
877
            mesh, propname, range.begin(), range.end(), init_value);
}

Max Lyon's avatar
Max Lyon committed
878
879
880
881
882
883
884
885
886
887

/** @relates PropertyManager
 * Returns a convenience wrapper around the points property of a mesh.
 */
template<typename MeshT>
PropertyManager<OpenMesh::VPropHandleT<typename MeshT::Point>>
getPointsProperty(MeshT &mesh) {
  return PropertyManager<OpenMesh::VPropHandleT<typename MeshT::Point>>(mesh, mesh.points_property_handle());
}

888
889
890
891
892
893
894
895
896
897
/** @relates PropertyManager
 * Returns a convenience wrapper around the points property of a mesh that only allows const access.
 */
template<typename MeshT>
ConstPropertyViewer<OpenMesh::VPropHandleT<typename MeshT::Point>>
getPointsProperty(const MeshT &mesh) {
  using PropType = OpenMesh::VPropHandleT<typename MeshT::Point>;
  return ConstPropertyViewer<PropType>(mesh, mesh.points_property_handle());
}

898
899
template <typename HandleT, typename T>
using Prop = PropertyManager<typename PropHandle<HandleT>::template type<T>>;
Max Lyon's avatar
Max Lyon committed
900
901
902
903
904
905
906
907
908
909
910
911
912

template <typename T>
using VProp = PropertyManager<OpenMesh::VPropHandleT<T>>;

template <typename T>
using HProp = PropertyManager<OpenMesh::HPropHandleT<T>>;

template <typename T>
using EProp = PropertyManager<OpenMesh::EPropHandleT<T>>;

template <typename T>
using FProp = PropertyManager<OpenMesh::FPropHandleT<T>>;

913
914
915
template <typename T>
using MProp = PropertyManager<OpenMesh::MPropHandleT<T>>;

Max Lyon's avatar
Max Lyon committed
916

917
918
} /* namespace OpenMesh */
#endif /* PROPERTYMANAGER_HH_ */