RavEngine
Loading...
Searching...
No Matches
decimate.h
1//----------------------------------------------------------------------------//
2// //
3// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
4// and distributed under the MIT License (MIT). //
5// //
6// Copyright (c) Guillaume Blanc //
7// //
8// Permission is hereby granted, free of charge, to any person obtaining a //
9// copy of this software and associated documentation files (the "Software"), //
10// to deal in the Software without restriction, including without limitation //
11// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
12// and/or sell copies of the Software, and to permit persons to whom the //
13// Software is furnished to do so, subject to the following conditions: //
14// //
15// The above copyright notice and this permission notice shall be included in //
16// all copies or substantial portions of the Software. //
17// //
18// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
19// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
20// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
21// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
22// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
23// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
24// DEALINGS IN THE SOFTWARE. //
25// //
26//----------------------------------------------------------------------------//
27
28#ifndef OZZ_ANIMATION_OFFLINE_DECIMATE_H_
29#define OZZ_ANIMATION_OFFLINE_DECIMATE_H_
30
31#ifndef OZZ_INCLUDE_PRIVATE_HEADER
32#error "This header is private, it cannot be included from public headers."
33#endif // OZZ_INCLUDE_PRIVATE_HEADER
34
35#include "ozz/base/containers/stack.h"
36#include "ozz/base/containers/vector.h"
37
38#include <cassert>
39
40namespace ozz {
41namespace animation {
42namespace offline {
43
44// Decimation algorithm based on Ramer–Douglas–Peucker.
45// https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm
46// _Track must have std::vector interface.
47// Adapter must have the following interface:
48// struct Adapter {
49// bool Decimable(const Key&) const;
50// Key Lerp(const Key& _left, const Key& _right, const Key& _ref) const;
51// float Distance(const Key& _a, const Key& _b) const;
52// };
53template <typename _Track, typename _Adapter>
54void Decimate(const _Track& _src, const _Adapter& _adapter, float _tolerance,
55 _Track* _dest) {
56 // Early out if not enough data.
57 if (_src.size() < 2) {
58 *_dest = _src;
59 return;
60 }
61
62 // Stack of segments to process.
63 typedef std::pair<size_t, size_t> Segment;
64 ozz::stack<Segment> segments;
65
66 // Bit vector of all points to included.
67 ozz::vector<bool> included(_src.size(), false);
68
69 // Pushes segment made from first and last points.
70 segments.push(Segment(0, _src.size() - 1));
71 included[0] = true;
72 included[_src.size() - 1] = true;
73
74 // Empties segments stack.
75 while (!segments.empty()) {
76 // Pops next segment to process.
77 const Segment segment = segments.top();
78 segments.pop();
79
80 // Looks for the furthest point from the segment.
81 float max = -1.f;
82 size_t candidate = segment.first;
83 typename _Track::const_reference left = _src[segment.first];
84 typename _Track::const_reference right = _src[segment.second];
85 for (size_t i = segment.first + 1; i < segment.second; ++i) {
86 assert(!included[i] && "Included points should be processed once only.");
87 typename _Track::const_reference test = _src[i];
88 if (!_adapter.Decimable(test)) {
89 candidate = i;
90 break;
91 } else {
92 const float distance =
93 _adapter.Distance(_adapter.Lerp(left, right, test), test);
94 if (distance > _tolerance && distance > max) {
95 max = distance;
96 candidate = i;
97 }
98 }
99 }
100
101 // If found, include the point and pushes the 2 new segments (before and
102 // after the new point).
103 if (candidate != segment.first) {
104 included[candidate] = true;
105 if (candidate - segment.first > 1) {
106 segments.push(Segment(segment.first, candidate));
107 }
108 if (segment.second - candidate > 1) {
109 segments.push(Segment(candidate, segment.second));
110 }
111 }
112 }
113
114 // Copy all included points.
115 _dest->clear();
116 for (size_t i = 0; i < _src.size(); ++i) {
117 if (included[i]) {
118 _dest->push_back(_src[i]);
119 }
120 }
121
122 // Removes last key if constant.
123 if (_dest->size() > 1) {
124 typename _Track::const_iterator end = _dest->end();
125 typename _Track::const_reference last = *(--end);
126 typename _Track::const_reference penultimate = *(--end);
127 const float distance = _adapter.Distance(penultimate, last);
128 if (_adapter.Decimable(last) && distance <= _tolerance) {
129 _dest->pop_back();
130 }
131 }
132}
133} // namespace offline
134} // namespace animation
135} // namespace ozz
136#endif // OZZ_ANIMATION_OFFLINE_DECIMATE_H_