fdo#74697 Add Bluez 5 support for impress remote.
[LibreOffice.git] / include / basegfx / range / b2dconnectedranges.hxx
blobf1d65196bd8f50d3ebffc1f27db02b009609e157
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #ifndef _BGFX_RANGE_B2DCONNECTEDRANGES_HXX
21 #define _BGFX_RANGE_B2DCONNECTEDRANGES_HXX
23 #include <osl/diagnose.h>
24 #include <basegfx/range/b2drange.hxx>
25 #include <list>
26 #include <utility>
27 #include <algorithm>
30 namespace basegfx
32 /** Calculate connected ranges from input ranges.
34 This template constructs a list of connected ranges from the
35 given input ranges. That is, the output will contain a set of
36 ranges, itself containing a number of input ranges, which will
37 be mutually non-intersecting.
39 Example:
40 <code>
41 -------------------
42 | -------|
43 | | ||
44 | --- | ||
45 | | | -------| --------
46 | | +--------- | | |
47 | --+ | | | |
48 | | | | --------
49 | ---------- |
50 -------------------
51 </code
53 Here, the outer rectangles represent the output
54 ranges. Contained are the input rectangles that comprise these
55 output ranges.
57 @tpl UserData
58 User data to be stored along with the range, to later identify
59 which range went into which connected component. Must be
60 assignable, default- and copy-constructible.
62 template< typename UserData > class B2DConnectedRanges
64 public:
65 /// Type of the basic entity (rect + user data)
66 typedef ::std::pair< B2DRange, UserData > ComponentType;
67 typedef ::std::list< ComponentType > ComponentListType;
69 /// List of (intersecting) components, plus overall bounds
70 struct ConnectedComponents
72 ComponentListType maComponentList;
73 B2DRange maTotalBounds;
76 typedef ::std::list< ConnectedComponents > ConnectedComponentsType;
79 /// Create the range calculator
80 B2DConnectedRanges() :
81 maDisjunctAggregatesList(),
82 maTotalBounds()
86 /** Query total bounds of all added ranges.
88 @return the union bound rect over all added ranges.
90 B2DRange getBounds() const
92 return maTotalBounds;
95 /** Add an additional range.
97 This method integrates a new range into the connected
98 ranges lists. The method has a worst-case time complexity
99 of O(n^2), with n denoting the number of already added
100 ranges (typically, for well-behaved input, it is O(n)
101 though).
103 void addRange( const B2DRange& rRange,
104 const UserData& rUserData )
106 // check whether fast path is possible: if new range is
107 // outside accumulated total range, can add it as a
108 // separate component right away.
109 const bool bNotOutsideEverything(
110 maTotalBounds.overlaps( rRange ) );
112 // update own global bounds range
113 maTotalBounds.expand( rRange );
115 // assemble anything intersecting with rRange into
116 // this new connected component
117 ConnectedComponents aNewConnectedComponent;
119 // as at least rRange will be a member of
120 // aNewConnectedComponent (will be added below), can
121 // preset the overall bounds here.
122 aNewConnectedComponent.maTotalBounds = rRange;
126 // STAGE 1: Search for intersecting maDisjunctAggregatesList entries
127 // =================================================================
130 // if rRange is empty, it will intersect with no
131 // maDisjunctAggregatesList member. Thus, we can safe us
132 // the check.
133 // if rRange is outside all other rectangle, skip here,
134 // too
135 if( bNotOutsideEverything &&
136 !rRange.isEmpty() )
138 typename ConnectedComponentsType::iterator aCurrAggregate;
139 typename ConnectedComponentsType::iterator aLastAggregate;
141 // flag, determining whether we touched one or more of
142 // the maDisjunctAggregatesList entries. _If_ we did,
143 // we have to repeat the intersection process, because
144 // these changes might have generated new
145 // intersections.
146 bool bSomeAggregatesChanged;
148 // loop, until bSomeAggregatesChanged stays false
151 // only continue loop if 'intersects' branch below was hit
152 bSomeAggregatesChanged = false;
154 // iterate over all current members of maDisjunctAggregatesList
155 for( aCurrAggregate=maDisjunctAggregatesList.begin(),
156 aLastAggregate=maDisjunctAggregatesList.end();
157 aCurrAggregate != aLastAggregate; )
159 // first check if current component's bounds
160 // are empty. This ensures that distinct empty
161 // components are not merged into one
162 // aggregate. As a matter of fact, they have
163 // no position and size.
165 if( !aCurrAggregate->maTotalBounds.isEmpty() &&
166 aCurrAggregate->maTotalBounds.overlaps(
167 aNewConnectedComponent.maTotalBounds ) )
169 // union the intersecting
170 // maDisjunctAggregatesList element into
171 // aNewConnectedComponent
173 // calc union bounding box
174 aNewConnectedComponent.maTotalBounds.expand( aCurrAggregate->maTotalBounds );
176 // extract all aCurrAggregate components
177 // to aNewConnectedComponent
178 aNewConnectedComponent.maComponentList.splice(
179 aNewConnectedComponent.maComponentList.end(),
180 aCurrAggregate->maComponentList );
182 // remove and delete aCurrAggregate entry
183 // from list (we've gutted it's content
184 // above). list::erase() will update our
185 // iterator with the predecessor here.
186 aCurrAggregate = maDisjunctAggregatesList.erase( aCurrAggregate );
188 // at least one aggregate changed, need to rescan everything
189 bSomeAggregatesChanged = true;
191 else
193 aCurrAggregate++;
197 while( bSomeAggregatesChanged );
201 // STAGE 2: Add newly generated connected component list element
202 // =============================================================
205 // add new component to the end of the component list
206 aNewConnectedComponent.maComponentList.push_back(
207 ComponentType( rRange, rUserData ) );
209 // do some consistency checks (aka post conditions)
210 OSL_ENSURE( !aNewConnectedComponent.maComponentList.empty(),
211 "B2DConnectedRanges::addRange(): empty aggregate list" );
212 OSL_ENSURE( !aNewConnectedComponent.maTotalBounds.isEmpty() ||
213 (aNewConnectedComponent.maTotalBounds.isEmpty() &&
214 aNewConnectedComponent.maComponentList.size() == 1),
215 "B2DConnectedRanges::addRange(): empty ranges must be solitary");
217 // add aNewConnectedComponent as a new entry to
218 // maDisjunctAggregatesList
219 maDisjunctAggregatesList.push_back( aNewConnectedComponent );
222 /** Apply a functor to each of the disjunct component
223 aggregates.
225 @param aFunctor
226 Functor to apply. Must provide an operator( const ConnectedComponents& ).
228 @return a copy of the functor, as applied to all aggregates.
230 template< typename UnaryFunctor > UnaryFunctor forEachAggregate( UnaryFunctor aFunctor ) const
232 return ::std::for_each( maDisjunctAggregatesList.begin(),
233 maDisjunctAggregatesList.end(),
234 aFunctor );
237 private:
238 // default: disabled copy/assignment
239 B2DConnectedRanges(const B2DConnectedRanges&);
240 B2DConnectedRanges& operator=( const B2DConnectedRanges& );
242 /** Current list of disjunct sets of connected components
244 Each entry corresponds to one of the top-level rectangles
245 in the drawing above.
247 ConnectedComponentsType maDisjunctAggregatesList;
249 /** Global bound rect over all added ranges.
251 B2DRange maTotalBounds;
255 #endif /* _BGFX_RANGE_B2DCONNECTEDRANGES_HXX */
257 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */