Bug 1444460 [wpt PR 9948] - gyroscope: Rename LocalCoordinateSystem to GyroscopeLocal...
[gecko.git] / dom / svg / SVGLengthListSMILType.cpp
blob8664965a2516ad0e78255b83f3400de3b46c0a3f
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "SVGLengthListSMILType.h"
8 #include "nsSMILValue.h"
9 #include "SVGLengthList.h"
10 #include "nsMathUtils.h"
11 #include "mozilla/FloatingPoint.h"
12 #include <math.h>
13 #include <algorithm>
15 namespace mozilla {
17 /*static*/ SVGLengthListSMILType SVGLengthListSMILType::sSingleton;
19 //----------------------------------------------------------------------
20 // nsISMILType implementation
22 void
23 SVGLengthListSMILType::Init(nsSMILValue &aValue) const
25 MOZ_ASSERT(aValue.IsNull(), "Unexpected value type");
27 SVGLengthListAndInfo* lengthList = new SVGLengthListAndInfo();
29 // See the comment documenting Init() in our header file:
30 lengthList->SetCanZeroPadList(true);
32 aValue.mU.mPtr = lengthList;
33 aValue.mType = this;
36 void
37 SVGLengthListSMILType::Destroy(nsSMILValue& aValue) const
39 NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value type");
40 delete static_cast<SVGLengthListAndInfo*>(aValue.mU.mPtr);
41 aValue.mU.mPtr = nullptr;
42 aValue.mType = nsSMILNullType::Singleton();
45 nsresult
46 SVGLengthListSMILType::Assign(nsSMILValue& aDest,
47 const nsSMILValue& aSrc) const
49 NS_PRECONDITION(aDest.mType == aSrc.mType, "Incompatible SMIL types");
50 NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL value");
52 const SVGLengthListAndInfo* src =
53 static_cast<const SVGLengthListAndInfo*>(aSrc.mU.mPtr);
54 SVGLengthListAndInfo* dest =
55 static_cast<SVGLengthListAndInfo*>(aDest.mU.mPtr);
57 return dest->CopyFrom(*src);
60 bool
61 SVGLengthListSMILType::IsEqual(const nsSMILValue& aLeft,
62 const nsSMILValue& aRight) const
64 NS_PRECONDITION(aLeft.mType == aRight.mType, "Incompatible SMIL types");
65 NS_PRECONDITION(aLeft.mType == this, "Unexpected type for SMIL value");
67 return *static_cast<const SVGLengthListAndInfo*>(aLeft.mU.mPtr) ==
68 *static_cast<const SVGLengthListAndInfo*>(aRight.mU.mPtr);
71 nsresult
72 SVGLengthListSMILType::Add(nsSMILValue& aDest,
73 const nsSMILValue& aValueToAdd,
74 uint32_t aCount) const
76 NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL type");
77 NS_PRECONDITION(aValueToAdd.mType == this, "Incompatible SMIL type");
79 SVGLengthListAndInfo& dest =
80 *static_cast<SVGLengthListAndInfo*>(aDest.mU.mPtr);
81 const SVGLengthListAndInfo& valueToAdd =
82 *static_cast<const SVGLengthListAndInfo*>(aValueToAdd.mU.mPtr);
84 // To understand this code, see the comments documenting our Init() method,
85 // and documenting SVGLengthListAndInfo::CanZeroPadList().
87 // Note that *this* method actually may safely zero pad a shorter list
88 // regardless of the value returned by CanZeroPadList() for that list,
89 // just so long as the shorter list is being added *to* the longer list
90 // and *not* vice versa! It's okay in the case of adding a shorter list to a
91 // longer list because during the add operation we'll end up adding the
92 // zeros to actual specified values. It's *not* okay in the case of adding a
93 // longer list to a shorter list because then we end up adding to implicit
94 // zeros when we'd actually need to add to whatever the underlying values
95 // should be, not zeros, and those values are not explicit or otherwise
96 // available.
98 if (valueToAdd.IsIdentity()) { // Adding identity value - no-op
99 return NS_OK;
102 if (dest.IsIdentity()) { // Adding *to* an identity value
103 if (!dest.SetLength(valueToAdd.Length())) {
104 return NS_ERROR_OUT_OF_MEMORY;
106 for (uint32_t i = 0; i < dest.Length(); ++i) {
107 dest[i].SetValueAndUnit(valueToAdd[i].GetValueInCurrentUnits() * aCount,
108 valueToAdd[i].GetUnit());
110 dest.SetInfo(valueToAdd.Element(), valueToAdd.Axis(),
111 valueToAdd.CanZeroPadList()); // propagate target element info!
112 return NS_OK;
114 MOZ_ASSERT(dest.Element() == valueToAdd.Element(),
115 "adding values from different elements...?");
117 // Zero-pad our |dest| list, if necessary.
118 if (dest.Length() < valueToAdd.Length()) {
119 if (!dest.CanZeroPadList()) {
120 // SVGContentUtils::ReportToConsole
121 return NS_ERROR_FAILURE;
124 MOZ_ASSERT(valueToAdd.CanZeroPadList(),
125 "values disagree about attribute's zero-paddibility");
127 uint32_t i = dest.Length();
128 if (!dest.SetLength(valueToAdd.Length())) {
129 return NS_ERROR_OUT_OF_MEMORY;
131 for (; i < valueToAdd.Length(); ++i) {
132 dest[i].SetValueAndUnit(0.0f, valueToAdd[i].GetUnit());
136 for (uint32_t i = 0; i < valueToAdd.Length(); ++i) {
137 float valToAdd;
138 if (dest[i].GetUnit() == valueToAdd[i].GetUnit()) {
139 valToAdd = valueToAdd[i].GetValueInCurrentUnits();
140 } else {
141 // If units differ, we use the unit of the item in 'dest'.
142 // We leave it to the frame code to check that values are finite.
143 valToAdd = valueToAdd[i].GetValueInSpecifiedUnit(dest[i].GetUnit(),
144 dest.Element(),
145 dest.Axis());
147 dest[i].SetValueAndUnit(
148 dest[i].GetValueInCurrentUnits() + valToAdd * aCount,
149 dest[i].GetUnit());
152 // propagate target element info!
153 dest.SetInfo(valueToAdd.Element(), valueToAdd.Axis(),
154 dest.CanZeroPadList() && valueToAdd.CanZeroPadList());
156 return NS_OK;
159 nsresult
160 SVGLengthListSMILType::ComputeDistance(const nsSMILValue& aFrom,
161 const nsSMILValue& aTo,
162 double& aDistance) const
164 NS_PRECONDITION(aFrom.mType == this, "Unexpected SMIL type");
165 NS_PRECONDITION(aTo.mType == this, "Incompatible SMIL type");
167 const SVGLengthListAndInfo& from =
168 *static_cast<const SVGLengthListAndInfo*>(aFrom.mU.mPtr);
169 const SVGLengthListAndInfo& to =
170 *static_cast<const SVGLengthListAndInfo*>(aTo.mU.mPtr);
172 // To understand this code, see the comments documenting our Init() method,
173 // and documenting SVGLengthListAndInfo::CanZeroPadList().
175 NS_ASSERTION((from.CanZeroPadList() == to.CanZeroPadList()) ||
176 (from.CanZeroPadList() && from.IsEmpty()) ||
177 (to.CanZeroPadList() && to.IsEmpty()),
178 "Only \"zero\" nsSMILValues from the SMIL engine should "
179 "return true for CanZeroPadList() when the attribute "
180 "being animated can't be zero padded");
182 if ((from.Length() < to.Length() && !from.CanZeroPadList()) ||
183 (to.Length() < from.Length() && !to.CanZeroPadList())) {
184 // SVGContentUtils::ReportToConsole
185 return NS_ERROR_FAILURE;
188 // We return the root of the sum of the squares of the deltas between the
189 // user unit values of the lengths at each correspanding index. In the
190 // general case, paced animation is probably not useful, but this strategy at
191 // least does the right thing for paced animation in the face of simple
192 // 'values' lists such as:
194 // values="100 200 300; 101 201 301; 110 210 310"
196 // I.e. half way through the simple duration we'll get "105 205 305".
198 double total = 0.0;
200 uint32_t i = 0;
201 for (; i < from.Length() && i < to.Length(); ++i) {
202 double f = from[i].GetValueInUserUnits(from.Element(), from.Axis());
203 double t = to[i].GetValueInUserUnits(to.Element(), to.Axis());
204 double delta = t - f;
205 total += delta * delta;
208 // In the case that from.Length() != to.Length(), one of the following loops
209 // will run. (OK since CanZeroPadList()==true for the other list.)
211 for (; i < from.Length(); ++i) {
212 double f = from[i].GetValueInUserUnits(from.Element(), from.Axis());
213 total += f * f;
215 for (; i < to.Length(); ++i) {
216 double t = to[i].GetValueInUserUnits(to.Element(), to.Axis());
217 total += t * t;
220 float distance = sqrt(total);
221 if (!IsFinite(distance)) {
222 return NS_ERROR_FAILURE;
224 aDistance = distance;
225 return NS_OK;
228 nsresult
229 SVGLengthListSMILType::Interpolate(const nsSMILValue& aStartVal,
230 const nsSMILValue& aEndVal,
231 double aUnitDistance,
232 nsSMILValue& aResult) const
234 NS_PRECONDITION(aStartVal.mType == aEndVal.mType,
235 "Trying to interpolate different types");
236 NS_PRECONDITION(aStartVal.mType == this,
237 "Unexpected types for interpolation");
238 NS_PRECONDITION(aResult.mType == this, "Unexpected result type");
240 const SVGLengthListAndInfo& start =
241 *static_cast<const SVGLengthListAndInfo*>(aStartVal.mU.mPtr);
242 const SVGLengthListAndInfo& end =
243 *static_cast<const SVGLengthListAndInfo*>(aEndVal.mU.mPtr);
244 SVGLengthListAndInfo& result =
245 *static_cast<SVGLengthListAndInfo*>(aResult.mU.mPtr);
247 // To understand this code, see the comments documenting our Init() method,
248 // and documenting SVGLengthListAndInfo::CanZeroPadList().
250 NS_ASSERTION((start.CanZeroPadList() == end.CanZeroPadList()) ||
251 (start.CanZeroPadList() && start.IsEmpty()) ||
252 (end.CanZeroPadList() && end.IsEmpty()),
253 "Only \"zero\" nsSMILValues from the SMIL engine should "
254 "return true for CanZeroPadList() when the attribute "
255 "being animated can't be zero padded");
257 if ((start.Length() < end.Length() && !start.CanZeroPadList()) ||
258 (end.Length() < start.Length() && !end.CanZeroPadList())) {
259 // SVGContentUtils::ReportToConsole
260 return NS_ERROR_FAILURE;
263 if (!result.SetLength(std::max(start.Length(), end.Length()))) {
264 return NS_ERROR_OUT_OF_MEMORY;
267 uint32_t i = 0;
268 for (; i < start.Length() && i < end.Length(); ++i) {
269 float s;
270 if (start[i].GetUnit() == end[i].GetUnit()) {
271 s = start[i].GetValueInCurrentUnits();
272 } else {
273 // If units differ, we use the unit of the item in 'end'.
274 // We leave it to the frame code to check that values are finite.
275 s = start[i].GetValueInSpecifiedUnit(end[i].GetUnit(), end.Element(), end.Axis());
277 float e = end[i].GetValueInCurrentUnits();
278 result[i].SetValueAndUnit(s + (e - s) * aUnitDistance, end[i].GetUnit());
281 // In the case that start.Length() != end.Length(), one of the following
282 // loops will run. (Okay, since CanZeroPadList()==true for the other list.)
284 for (; i < start.Length(); ++i) {
285 result[i].SetValueAndUnit(start[i].GetValueInCurrentUnits() -
286 start[i].GetValueInCurrentUnits() * aUnitDistance,
287 start[i].GetUnit());
289 for (; i < end.Length(); ++i) {
290 result[i].SetValueAndUnit(end[i].GetValueInCurrentUnits() * aUnitDistance,
291 end[i].GetUnit());
294 // propagate target element info!
295 result.SetInfo(end.Element(), end.Axis(),
296 start.CanZeroPadList() && end.CanZeroPadList());
298 return NS_OK;
301 } // namespace mozilla