View Javadoc
1   /*
2    * Copyright (c) 2002-2025 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.htmlunit.javascript.host.svg;
16  
17  import org.htmlunit.javascript.HtmlUnitScriptable;
18  import org.htmlunit.javascript.JavaScriptEngine;
19  import org.htmlunit.javascript.configuration.JsxClass;
20  import org.htmlunit.javascript.configuration.JsxConstructor;
21  import org.htmlunit.javascript.configuration.JsxFunction;
22  import org.htmlunit.javascript.configuration.JsxGetter;
23  import org.htmlunit.javascript.configuration.JsxSetter;
24  import org.htmlunit.javascript.host.Window;
25  import org.htmlunit.javascript.host.dom.DOMException;
26  
27  /**
28   * A JavaScript object for {@code SVGMatrix}.
29   * @see <a href="https://developer.mozilla.org/en-US/docs/DOM/SVGMatrix">MDN doc</a>
30   * @author Marc Guillemot
31   * @author Ronald Brill
32   */
33  @JsxClass
34  public class SVGMatrix extends HtmlUnitScriptable {
35  
36      private double shearX_;
37      private double shearY_;
38      private double scaleX_;
39      private double scaleY_;
40      private double translateX_;
41      private double translateY_;
42  
43      /**
44       * Creates an instance.
45       */
46      public SVGMatrix() {
47          super();
48  
49          shearX_ = 0.0;
50          shearY_ = 0.0;
51          scaleX_ = 1.0;
52          scaleY_ = 1.0;
53          translateX_ = 0.0;
54          translateY_ = 0.0;
55      }
56  
57      /**
58       * JavaScript constructor.
59       */
60      @JsxConstructor
61      public void jsConstructor() {
62          // nothing to do
63      }
64  
65      /**
66       * Instantiates and configure scope and prototype.
67       * @param scope the parent scope
68       */
69      public SVGMatrix(final Window scope) {
70          this();
71          setParentScope(scope);
72          setPrototype(getPrototype(getClass()));
73      }
74  
75      /**
76       * Gets the <code>a</code> entry of the matrix.
77       * @return the field
78       */
79      @JsxGetter
80      public double getA() {
81          return scaleX_;
82      }
83  
84      /**
85       * Gets the <code>b</code> entry of the matrix.
86       * @return the field
87       */
88      @JsxGetter
89      public double getB() {
90          return shearY_;
91      }
92  
93      /**
94       * Gets the <code>c</code> entry of the matrix.
95       * @return the field
96       */
97      @JsxGetter
98      public double getC() {
99          return shearX_;
100     }
101 
102     /**
103      * Gets the <code>d</code> entry of the matrix.
104      * @return the field
105      */
106     @JsxGetter
107     public double getD() {
108         return scaleY_;
109     }
110 
111     /**
112      * Gets the <code>e</code> entry of the matrix.
113      * @return the field
114      */
115     @JsxGetter
116     public double getE() {
117         return translateX_;
118     }
119 
120     /**
121      * Gets the <code>f</code> entry of the matrix.
122      * @return the field
123      */
124     @JsxGetter
125     public double getF() {
126         return translateY_;
127     }
128 
129     /**
130      * Sets the <code>a</code> entry of the matrix.
131      * @param newValue the new value for the field
132      */
133     @JsxSetter
134     public void setA(final double newValue) {
135         scaleX_ = newValue;
136     }
137 
138     /**
139      * Sets the <code>b</code> entry of the matrix.
140      * @param newValue the new value for the field
141      */
142     @JsxSetter
143     public void setB(final double newValue) {
144         shearY_ = newValue;
145     }
146 
147     /**
148      * Sets the <code>c</code> entry of the matrix.
149      * @param newValue the new value for the field
150      */
151     @JsxSetter
152     public void setC(final double newValue) {
153         shearX_ = newValue;
154     }
155 
156     /**
157      * Sets the <code>d</code> entry of the matrix.
158      * @param newValue the new value for the field
159      */
160     @JsxSetter
161     public void setD(final double newValue) {
162         scaleY_ = newValue;
163     }
164 
165     /**
166      * Sets the <code>e</code> entry of the matrix.
167      * @param newValue the new value for the field
168      */
169     @JsxSetter
170     public void setE(final double newValue) {
171         translateX_ = newValue;
172     }
173 
174     /**
175      * Sets the <code>f</code> entry of the matrix.
176      * @param newValue the new value for the field
177      */
178     @JsxSetter
179     public void setF(final double newValue) {
180         translateY_ = newValue;
181     }
182 
183     /**
184      * Transforms the matrix.
185      * @return the resulting matrix
186      */
187     @JsxFunction
188     public SVGMatrix flipX() {
189         final SVGMatrix result = new SVGMatrix(getWindow());
190         result.shearX_ = shearX_;
191         result.shearY_ = -shearY_;
192         result.scaleX_ = -scaleX_;
193         result.scaleY_ = scaleY_;
194         result.translateX_ = translateX_;
195         result.translateY_ = translateY_;
196 
197         return result;
198     }
199 
200     /**
201      * Transforms the matrix.
202      * @return the resulting matrix
203      */
204     @JsxFunction
205     public SVGMatrix flipY() {
206         final SVGMatrix result = new SVGMatrix(getWindow());
207         result.shearX_ = -shearX_;
208         result.shearY_ = shearY_;
209         result.scaleX_ = scaleX_;
210         result.scaleY_ = -scaleY_;
211         result.translateX_ = translateX_;
212         result.translateY_ = translateY_;
213 
214         return result;
215     }
216 
217     /**
218      * Transforms the matrix.
219      * @return the resulting matrix
220      */
221     @JsxFunction
222     public SVGMatrix inverse() {
223         final double determinant = scaleX_ * scaleY_ - shearX_ * shearY_;
224 
225         if (Math.abs(determinant) < 1E-10) {
226             throw JavaScriptEngine.asJavaScriptException(
227                     getWindow(),
228                     "Failed to execute 'inverse' on 'SVGMatrix': The matrix is not invertible.",
229                     DOMException.INVALID_STATE_ERR);
230         }
231 
232         final SVGMatrix result = new SVGMatrix(getWindow());
233         result.shearX_ = -shearX_ / determinant;
234         result.shearY_ = -shearY_ / determinant;
235         result.scaleX_ = scaleY_ / determinant;
236         result.scaleY_ = scaleX_ / determinant;
237         result.translateX_ = (shearX_ * translateY_ - scaleY_ * translateX_) / determinant;
238         result.translateY_ = (shearY_ * translateX_ - scaleX_ * translateY_) / determinant;
239 
240         return result;
241     }
242 
243     /**
244      * Transforms the matrix.
245      * @param by the matrix to multiply by
246      * @return the resulting matrix
247      */
248     @JsxFunction
249     public SVGMatrix multiply(final SVGMatrix by) {
250         final SVGMatrix result = new SVGMatrix(getWindow());
251 
252         result.shearX_ = by.shearX_ * scaleX_ + by.scaleY_ * shearX_;
253         result.shearY_ = by.scaleX_ * shearY_ + by.shearY_ * scaleY_;
254         result.scaleX_ = by.scaleX_ * scaleX_ + by.shearY_ * shearX_;
255         result.scaleY_ = by.shearX_ * shearY_ + by.scaleY_ * scaleY_;
256         result.translateX_ = by.translateX_ * scaleX_ + by.translateY_ * shearX_ + translateX_;
257         result.translateY_ = by.translateX_ * shearY_ + by.translateY_ * scaleY_ + translateY_;
258 
259         return result;
260     }
261 
262     /**
263      * Rotates the matrix.
264      * @param angle the rotation angle
265      * @return the resulting matrix
266      */
267     @JsxFunction
268     public SVGMatrix rotate(final double angle) {
269         final double theta = Math.toRadians(angle);
270         final double sin = Math.sin(theta);
271         final double cos = Math.cos(theta);
272 
273         final SVGMatrix result = new SVGMatrix(getWindow());
274 
275         result.shearX_ = -sin * scaleX_ + cos * shearX_;
276         result.shearY_ = cos * shearY_ + sin * scaleY_;
277         result.scaleX_ = cos * scaleX_ + sin * shearX_;
278         result.scaleY_ = -sin * shearY_ + cos * scaleY_;
279         result.translateX_ = translateX_;
280         result.translateY_ = translateY_;
281 
282         return result;
283     }
284 
285     /**
286      * Transforms the matrix.
287      * @param x the x-coordinate of the vector
288      * @param y the y-coordinate of the vector
289      * @return the resulting matrix
290      */
291     @JsxFunction
292     public SVGMatrix rotateFromVector(final double x, final double y) {
293         if (x == 0 || y == 0) {
294             throw JavaScriptEngine.asJavaScriptException(
295                     getWindow(),
296                     "Failed to execute 'rotateFromVector' on 'SVGMatrix': Arguments cannot be zero.",
297                     DOMException.INVALID_ACCESS_ERR);
298         }
299 
300         final double theta = Math.atan2(y, x);
301         final double sin = Math.sin(theta);
302         final double cos = Math.cos(theta);
303 
304         final SVGMatrix result = new SVGMatrix(getWindow());
305 
306         result.shearX_ = -sin * scaleX_ + cos * shearX_;
307         result.shearY_ = cos * shearY_ + sin * scaleY_;
308         result.scaleX_ = cos * scaleX_ + sin * shearX_;
309         result.scaleY_ = -sin * shearY_ + cos * scaleY_;
310         result.translateX_ = translateX_;
311         result.translateY_ = translateY_;
312 
313         return result;
314     }
315 
316     /**
317      * Transforms the matrix.
318      * @param factor the scale factor
319      * @return the resulting matrix
320      */
321     @JsxFunction
322     public SVGMatrix scale(final double factor) {
323         return scaleNonUniform(factor, factor);
324     }
325 
326     /**
327      * Transforms the matrix.
328      * @param factorX the factor for the x-axis
329      * @param factorY the factor for the y-axis
330      * @return the resulting matrix
331      */
332     @JsxFunction
333     public SVGMatrix scaleNonUniform(final double factorX, final double factorY) {
334         final SVGMatrix result = new SVGMatrix(getWindow());
335 
336         result.shearX_ = factorY * shearX_;
337         result.shearY_ = factorX * shearY_;
338         result.scaleX_ = factorX * scaleX_;
339         result.scaleY_ = factorY * scaleY_;
340         result.translateX_ = translateX_;
341         result.translateY_ = translateY_;
342 
343         return result;
344     }
345 
346     /**
347      * Transforms the matrix.
348      * @param angle the skew angle
349      * @return the resulting matrix
350      */
351     @JsxFunction
352     public SVGMatrix skewX(final double angle) {
353         final double shear = Math.tan(Math.toRadians(angle));
354 
355         final SVGMatrix result = new SVGMatrix(getWindow());
356 
357         result.shearX_ = shear * scaleX_ + shearX_;
358         result.shearY_ = shearY_;
359         result.scaleX_ = scaleX_;
360         result.scaleY_ = shear * shearY_ + scaleY_;
361         result.translateX_ = translateX_;
362         result.translateY_ = translateY_;
363 
364         return result;
365     }
366 
367     /**
368      * Transforms the matrix.
369      * @param angle the skew angle
370      * @return the resulting matrix
371      */
372     @JsxFunction
373     public SVGMatrix skewY(final double angle) {
374         final double shear = Math.tan(Math.toRadians(angle));
375 
376         final SVGMatrix result = new SVGMatrix(getWindow());
377 
378         result.shearX_ = shearX_;
379         result.shearY_ = shearY_ + shear * scaleY_;
380         result.scaleX_ = scaleX_ + shear * shearX_;
381         result.scaleY_ = scaleY_;
382         result.translateX_ = translateX_;
383         result.translateY_ = translateY_;
384 
385         return result;
386     }
387 
388     /**
389      * Translates the matrix.
390      * @param x the distance along the x-axis
391      * @param y the distance along the y-axis
392      * @return the resulting matrix
393      */
394     @JsxFunction
395     public SVGMatrix translate(final double x, final double y) {
396         final SVGMatrix result = new SVGMatrix(getWindow());
397 
398         result.shearX_ = shearX_;
399         result.shearY_ = shearY_;
400         result.scaleX_ = scaleX_;
401         result.scaleY_ = scaleY_;
402         result.translateX_ = x * scaleX_ + y * shearX_ + translateX_;
403         result.translateY_ = x * shearY_ + y * scaleY_ + translateY_;
404 
405         return result;
406     }
407 }