xref: /trunk/main/basegfx/source/curve/b2dbeziertools.cxx (revision a3cdc23e488c57f3433f22cd4458e65c27aa499c)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 // MARKER(update_precomp.py): autogen include statement, do not remove
23 #include "precompiled_basegfx.hxx"
24 #include <basegfx/curve/b2dbeziertools.hxx>
25 #include <basegfx/curve/b2dcubicbezier.hxx>
26 #include <algorithm>
27 
28 namespace basegfx
29 {
30     B2DCubicBezierHelper::B2DCubicBezierHelper(const B2DCubicBezier& rBase, sal_uInt32 nDivisions)
31     :   maLengthArray(),
32         mnEdgeCount(0)
33     {
34         const bool bIsBezier(rBase.isBezier());
35 
36         if(bIsBezier)
37         {
38             // check nDivisions; at least one is needed, but also prevent too big values
39             if(nDivisions < 1)
40             {
41                 nDivisions = 1;
42             }
43             else if(nDivisions > 1000)
44             {
45                 nDivisions = 1000;
46             }
47 
48             // set nEdgeCount
49             mnEdgeCount = nDivisions + 1;
50 
51             // fill in maLengthArray
52             maLengthArray.clear();
53             maLengthArray.reserve(mnEdgeCount);
54             B2DPoint aCurrent(rBase.getStartPoint());
55             double fLength(0.0);
56 
57             for(sal_uInt32 a(1);;)
58             {
59                 const B2DPoint aNext(rBase.interpolatePoint((double)a / (double)mnEdgeCount));
60                 const B2DVector aEdge(aNext - aCurrent);
61 
62                 fLength += aEdge.getLength();
63                 maLengthArray.push_back(fLength);
64 
65                 if(++a < mnEdgeCount)
66                 {
67                     aCurrent = aNext;
68                 }
69                 else
70                 {
71                     const B2DPoint aLastNext(rBase.getEndPoint());
72                     const B2DVector aLastEdge(aLastNext - aNext);
73 
74                     fLength += aLastEdge.getLength();
75                     maLengthArray.push_back(fLength);
76                     break;
77                 }
78             }
79         }
80         else
81         {
82             maLengthArray.clear();
83             maLengthArray.push_back(rBase.getEdgeLength());
84             mnEdgeCount = 1;
85         }
86     }
87 
88     double B2DCubicBezierHelper::distanceToRelative(double fDistance) const
89     {
90         if(fDistance <= 0.0)
91         {
92             return 0.0;
93         }
94 
95         const double fLength(getLength());
96 
97         if(fTools::moreOrEqual(fDistance, fLength))
98         {
99             return 1.0;
100         }
101 
102         // fDistance is in ]0.0 .. fLength[
103 
104         if(1 == mnEdgeCount)
105         {
106             // not a bezier, linear edge
107             return fDistance / fLength;
108         }
109 
110         // it is a bezier
111         ::std::vector< double >::const_iterator aIter = ::std::lower_bound(maLengthArray.begin(), maLengthArray.end(), fDistance);
112         const sal_uInt32 nIndex(aIter - maLengthArray.begin());
113         const double fHighBound(maLengthArray[nIndex]);
114         const double fLowBound(nIndex ? maLengthArray[nIndex - 1] : 0.0);
115         const double fLinearInterpolatedLength((fDistance - fLowBound) / (fHighBound - fLowBound));
116 
117         return (static_cast< double >(nIndex) + fLinearInterpolatedLength) / static_cast< double >(mnEdgeCount);
118     }
119 
120     double B2DCubicBezierHelper::relativeToDistance(double fRelative) const
121     {
122         if(fRelative <= 0.0)
123         {
124             return 0.0;
125         }
126 
127         const double fLength(getLength());
128 
129         if(fTools::moreOrEqual(fRelative, 1.0))
130         {
131             return fLength;
132         }
133 
134         // fRelative is in ]0.0 .. 1.0[
135 
136         if(1 == mnEdgeCount)
137         {
138             // not a bezier, linear edge
139             return fRelative * fLength;
140         }
141 
142         // fRelative is in ]0.0 .. 1.0[
143         const double fIndex(fRelative * static_cast< double >(mnEdgeCount));
144         double fIntIndex;
145         const double fFractIndex(modf(fIndex, &fIntIndex));
146         const sal_uInt32 nIntIndex(static_cast< sal_uInt32 >(fIntIndex));
147         const double fStartDistance(nIntIndex ? maLengthArray[nIntIndex - 1] : 0.0);
148 
149         return fStartDistance + ((maLengthArray[nIntIndex] - fStartDistance) * fFractIndex);
150     }
151 } // end of namespace basegfx
152 
153 /* vim: set noet sw=4 ts=4: */
154