@@ -2465,6 +2465,247 @@ Comparison Functions: ''min()'', ''max()'', and ''clamp()''</h3>
2465
2465
</div>
2466
2466
2467
2467
2468
+ <h3 id=trig-funcs>
2469
+ Trigonometric Functions: ''sin()'', ''cos()'', ''tan()'', ''asin()'', ''acos()'', ''atan()'', and ''atan2()''</h3>
2470
+
2471
+ The trigonometric functions--
2472
+ ''sin()'' , ''cos()'' , ''tan()'' , ''asin()'' , ''acos()'' , ''atan()'' , and ''atan2()'' --
2473
+ compute the various basic trigonometric relationships.
2474
+
2475
+ The <dfn lt="sin()">sin(A)</dfn> , <dfn lt="cos(A)">cos()</dfn> , and <dfn lt="tan()">tan(A)</dfn> functions
2476
+ all contain a single [=calculation=]
2477
+ which must resolve to either a <<number>>
2478
+ or an <<angle>> ,
2479
+ and compute their corresponding function
2480
+ by interpreting the result of their argument as radians.
2481
+ (That is, ''sin(45deg)'' , ''sin(.125)'' , and ''sin(3.14159 / 4 * 1rad)''
2482
+ all represent the same value,
2483
+ approximately ''.707'' .)
2484
+ They all represent a <<number>> ;
2485
+ ''sin()'' and ''cos()'' will always return a number between 0 and 1,
2486
+ while ''tan()'' can return any number between +∞ and −∞.
2487
+ (See [[#calc-type-checking]] for details on how [=math functions=] handle ∞.)
2488
+
2489
+ The <dfn lt="asin()">asin(A)</dfn> , <dfn lt="acos()">acos(A)</dfn> , and <dfn lt="atan()">atan(A)</dfn> functions
2490
+ are the "arc" or "inverse" trigonometric functions,
2491
+ representing the inverse function to their corresponding "normal" trig functions.
2492
+ All of them contain a single [=calculation=]
2493
+ which must resolve to a <<number>> ,
2494
+ and compute their corresponding function,
2495
+ interpreting their result as a number of radians,
2496
+ representing an <<angle>> .
2497
+ The angle returned by ''asin()'' must be normalized to the range [''-90deg'', ''90deg''] ;
2498
+ the angle returned by ''acos()'' to the range [''0deg'', ''180deg''] ;
2499
+ and the angle returned by ''atan()'' to the range [''-90deg'', ''90deg''] .
2500
+
2501
+ The <dfn lt="atan2()">atan2(A, B)</dfn> function
2502
+ contains two comma-separated [=calculations=] A and B
2503
+ that must resolve to <<number>> s,
2504
+ and returns the <<angle>>
2505
+ between the positive X-axis and the point (B,A).
2506
+ The returned angle must be normalized to the interval (''-180deg'' , ''180deg'' ]
2507
+ (that is, greater than ''-180deg'' , and less than or equal to ''180deg'' ).
2508
+
2509
+ Note: ''atan2(Y, X)'' is <em> generally</em> equivalent to ''atan(Y / X)'' ,
2510
+ but it gives a better answer when the point in question is in the second (NW) or third (SW) quadrants of the plane.
2511
+ ''atan2(1, -1)'' , corresponding to the point (-1, 1) in the NW of the plane,
2512
+ returns ''135deg'' ,
2513
+ distinct from ''atan2(-1, 1)'' , corresponding to the point (1, -1) in the SE of the plane,
2514
+ which returns ''-45deg'' .
2515
+ However, as ''1 / -1'' and ''-1 / 1'' both resolve to ''-1'' ,
2516
+ ''atan()'' returns the same ''-45deg'' for both points.
2517
+
2518
+
2519
+ <h4 id="trig-infinities">
2520
+ Argument Ranges</h4>
2521
+
2522
+ In ''sin(A)'' , ''cos(A)'' , or ''tan(A)'' ,
2523
+ if A is infinite,
2524
+ the result is NaN.
2525
+ (See [[#calc-type-checking]] for details on how [=math functions=] handle NaN.)
2526
+
2527
+ In ''asin(A)'' or ''acos(A)'' ,
2528
+ if A is less than -1 or greater than 1,
2529
+ the result is NaN.
2530
+
2531
+ In ''atan(A)'' ,
2532
+ if A is +∞,
2533
+ the result is ''90deg'' ;
2534
+ if A is -∞,
2535
+ the result is ''-90deg'' .
2536
+
2537
+ In ''atan2(A, B)'' ,
2538
+ if A and B are both zero,
2539
+ the result is ''0deg'' .
2540
+ If either or both [=calculations=] are infinite,
2541
+ the function must return an angle
2542
+ as if the infinite value was replaced by ''1'' (for +∞) or ''-1'' (for -∞)
2543
+ and the finite value was replaced by ''0'' :
2544
+ ''atan2(finite, ∞)'' must return ''0deg'' , as if it were ''atan2(0, 1)'' ;
2545
+ ''atan2(∞, ∞)'' must return ''45deg'' , as if it were ''atan2(1, 1)'' ;
2546
+ and so on around the circle.
2547
+
2548
+ Note: All of these behaviors are intended to match the "standard" definitions of these functions
2549
+ as implemented by most programming languages,
2550
+ in particular as implemented in JS.
2551
+
2552
+
2553
+ <h3 id=exponent-funcs>
2554
+ Exponential Functions: ''pow()'', ''sqrt()'', ''hypot()''</h3>
2555
+
2556
+ The exponential functions--
2557
+ ''pow()'' , ''sqrt()'' , and ''hypot()'' --
2558
+ compute various exponential functions with their arguments.
2559
+
2560
+ The <dfn lt="pow()">pow(A, B)</dfn> function
2561
+ contains two comma-separated [=calculations=] A and B,
2562
+ both of which must resolve to <<number>> s,
2563
+ and returns the result of raising A to the power of B,
2564
+ returning the value as a <<number>> .
2565
+
2566
+ The <dfn lt="sqrt()">sqrt(A)</dfn> function
2567
+ contains a single [=calculation=]
2568
+ which must resolve to a <<number>> ,
2569
+ and returns the square root of the value
2570
+ as a <<number>> .
2571
+ (''sqrt(X)'' and ''pow(X, .5)'' are equivalent;
2572
+ ''sqrt()'' is a common enough function
2573
+ that it is provided as a convenience.)
2574
+
2575
+ The <dfn lt="hypot()">hypot(A,B)</dfn> function
2576
+ contains two comma-separated [=calculations=] A and B,
2577
+ and returns the length of the hypotenuse of a right-angled triangle
2578
+ with legs equal to A and B.
2579
+ A and B can resolve to any <<number>> , <<dimension>> , or <<percentage>> ,
2580
+ but must have the <em> same</em> [=determine the type of a calculation|type=] ,
2581
+ or else the function is invalid;
2582
+ the result will have the same [=CSSNumericValue/type=] as the arguments.
2583
+
2584
+ <details class=note>
2585
+ <summary> Why does ''hypot()'' allow dimensions (values with units), but ''pow()'' and ''sqrt()'' only work on numbers?</summary>
2586
+
2587
+ You are allowed to write expressions like ''hypot(30px, 40px)'' ,
2588
+ which resolves to ''50px'' ,
2589
+ but you aren't allowed to write the expression
2590
+ ''sqrt(pow(30px, 2) + pow(40px, 2))'' ,
2591
+ despite the two being equivalent in most mathematical systems.
2592
+
2593
+ There are two reasons for this:
2594
+ numeric precision in the exponents,
2595
+ and clashing expectations from authors.
2596
+
2597
+ First, numerical precision.
2598
+ For a [=CSSNumericValue/type=] to [=CSSNumericValue/match=] a CSS production like <<length>> ,
2599
+ it needs to have a single unit with its exponent set to exactly 1.
2600
+ Theoretically, expressions like ''pow(pow(30px, 3), 1/3)'' should result in exactly that:
2601
+ the inner ''pow(30px, 3)'' would resolve to a value of 27000 with a [=CSSNumericValue/type=] of «[ "length" → 3 ] »
2602
+ (aka <<length>> ³),
2603
+ and then the ''pow(X, 1/3)'' would cube-root the value back down to 30 and multiply the exponent by 1/3,
2604
+ giving «[ "length" → 1 ] »,
2605
+ which [=CSSNumericValue/matches=] <<length>> .
2606
+ In the realm of pure mathematics, that's guaranteed to work out;
2607
+ in the real-world of computers using binary floating-point arithmetic,
2608
+ in some cases the powers might not exactly cancel out,
2609
+ leaving you with an invalid [=math function=]
2610
+ for confusing, hard-to-track-down reasons.
2611
+ (For a JS example,
2612
+ evaluate <code> Math.pow(Math.pow(30, 10/3), .1+.1+.1)</code> ;
2613
+ the result is not exactly 30,
2614
+ because <code> .1+.1+.1</code> is not exactly 3/10.
2615
+ Instead, <code> (10/3) * (.1 + .1 + .1)</code> is <em> slightly greater</em> than 1.)
2616
+
2617
+ Requiring authors to cast their value down into a number,
2618
+ do all the math on the raw number,
2619
+ then finally send it back to the desired unit,
2620
+ while inconvenient,
2621
+ ensures that numerical precision won't bite anyone:
2622
+ ''calc(pow(pow(30px / 1px, 3), 1/3) * 1px)'' is guaranteed to resolve to a <<length>> ,
2623
+ with a value that, if not exactly 30, is at least very close to 30,
2624
+ even if numerical precision actually prevents the powers from exactly canceling.
2625
+
2626
+ Second, clashing expectations.
2627
+ It's not uncommon for authors to expect ''pow(30px, 2)''
2628
+ to result in ''900px''
2629
+ (such as in <a href="https://github.com/sass/sass/issues/684">this Sass issue</a> );
2630
+ that is,
2631
+ just squaring the numerical value
2632
+ and leaving the unit alone.
2633
+ This, however, means the result is dependent on what unit you're expressing the argument in;
2634
+ if ''1em'' is ''16px'' ,
2635
+ then ''pow(1em, 2)'' would give ''1em'' ,
2636
+ while ''pow(16px, 2)'' would give ''256px'' , or ''16em'' ,
2637
+ which are very different values for what should otherwise be identical input arguments!
2638
+ This sort of input dependency is troublesome for CSS,
2639
+ which generally allows values to be [=canonical unit|canonicalized=] freely;
2640
+ it also makes more complex expressions like ''pow(2em + 10px, 2)'' difficult to interpret.
2641
+
2642
+ Again, requiring authors to cast their value down into a number
2643
+ and then back up again into the desired unit
2644
+ sidesteps these issues;
2645
+ ''pow(30, 2)'' is indeed ''900'' ,
2646
+ and the author can interpret that however they wish.
2647
+
2648
+ <hr>
2649
+
2650
+ On the other hand, ''hypot()'' doesn't suffer from these problems.
2651
+ Numerical precision in units isn't a concern,
2652
+ as the inputs and output all have the same type.
2653
+ The result isn't unit-dependent, either,
2654
+ due to the nature of the operation;
2655
+ ''hypot(3em, 4em)'' and ''hypot(48px, 64px)'' give the same result in both cases:
2656
+ ''5em'' or ''80px'' .
2657
+ Thus it's fine to let author use dimensions directly in ''hypot()'' .
2658
+ </details>
2659
+
2660
+ <h4 id="exponent-infinities">
2661
+ Argument Ranges</h4>
2662
+
2663
+ In ''pow(A, B)'' :
2664
+
2665
+ <dl class=switch>
2666
+ : if A is ±∞ and B is 0
2667
+ :: the result is 1
2668
+ : if A is +∞ and B is greater than 0
2669
+ :: the result is +∞
2670
+ : if A is +∞ and B is less than 0
2671
+ :: the result is 0
2672
+ : if A is -∞ and B is greater than 0
2673
+ :: the result is -∞ if B is an odd integer, +∞ otherwise
2674
+ : if A is -∞ and B is less than 0
2675
+ :: the result is 0⁻ if B is an odd integer, 0⁺ otherwise
2676
+
2677
+ : if A is in the (exclusive) range (-1, 1) and B is +∞
2678
+ :: the result is 0⁺
2679
+ : if A is 1 or -1 and B is +∞
2680
+ :: the result is NaN
2681
+ : if A is less than -1 or greater than 1, and B is +∞
2682
+ :: the result is +∞
2683
+
2684
+ : if A is in the (exclusive) range (-1, 1) and B is -∞
2685
+ :: the result is +∞
2686
+ : if A is 1 or -1 and B is -∞
2687
+ :: the result is NaN
2688
+ : if A is less than -1 or greater than 1, and B is -∞
2689
+ :: the result is 0⁺
2690
+ </dl>
2691
+
2692
+ In ''sqrt(A)'' ,
2693
+ if A is +∞,
2694
+ the result is +∞.
2695
+ If A is less than 0,
2696
+ the result is NaN.
2697
+
2698
+ In ''hypot(A, B)'' ,
2699
+ if either or both values are infinite,
2700
+ the result is +∞.
2701
+
2702
+ (See [[#calc-type-checking]] for details on how [=math functions=] handle NaN and infinities.)
2703
+
2704
+ Note: All of these behaviors are intended to match the "standard" definitions of these functions
2705
+ as implemented by most programming languages,
2706
+ in particular as implemented in JS.
2707
+
2708
+
2468
2709
<h3 id='calc-syntax'>
2469
2710
Syntax</h3>
2470
2711
0 commit comments