1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2015 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "include/core/SkPoint3.h"
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci// Returns the square of the Euclidian distance to (x,y,z).
11cb93a386Sopenharmony_cistatic inline float get_length_squared(float x, float y, float z) {
12cb93a386Sopenharmony_ci    return x * x + y * y + z * z;
13cb93a386Sopenharmony_ci}
14cb93a386Sopenharmony_ci
15cb93a386Sopenharmony_ci// Calculates the square of the Euclidian distance to (x,y,z) and stores it in
16cb93a386Sopenharmony_ci// *lengthSquared.  Returns true if the distance is judged to be "nearly zero".
17cb93a386Sopenharmony_ci//
18cb93a386Sopenharmony_ci// This logic is encapsulated in a helper method to make it explicit that we
19cb93a386Sopenharmony_ci// always perform this check in the same manner, to avoid inconsistencies
20cb93a386Sopenharmony_ci// (see http://code.google.com/p/skia/issues/detail?id=560 ).
21cb93a386Sopenharmony_cistatic inline bool is_length_nearly_zero(float x, float y, float z, float *lengthSquared) {
22cb93a386Sopenharmony_ci    *lengthSquared = get_length_squared(x, y, z);
23cb93a386Sopenharmony_ci    return *lengthSquared <= (SK_ScalarNearlyZero * SK_ScalarNearlyZero);
24cb93a386Sopenharmony_ci}
25cb93a386Sopenharmony_ci
26cb93a386Sopenharmony_civoid SkPoint3::dump(std::string& desc, int depth) const {
27cb93a386Sopenharmony_ci    std::string split(depth, '\t');
28cb93a386Sopenharmony_ci    desc += split + "\n SkPoint3:{ \n";
29cb93a386Sopenharmony_ci    desc += split + "\t fX: " + std::to_string(fX) + "\n";
30cb93a386Sopenharmony_ci    desc += split + "\t fY: " + std::to_string(fY) + "\n";
31cb93a386Sopenharmony_ci    desc += split + "\t fZ: " + std::to_string(fZ) + "\n";
32cb93a386Sopenharmony_ci    desc += split + "}\n";
33cb93a386Sopenharmony_ci}
34cb93a386Sopenharmony_ci
35cb93a386Sopenharmony_ciSkScalar SkPoint3::Length(SkScalar x, SkScalar y, SkScalar z) {
36cb93a386Sopenharmony_ci    float magSq = get_length_squared(x, y, z);
37cb93a386Sopenharmony_ci    if (SkScalarIsFinite(magSq)) {
38cb93a386Sopenharmony_ci        return sk_float_sqrt(magSq);
39cb93a386Sopenharmony_ci    } else {
40cb93a386Sopenharmony_ci        double xx = x;
41cb93a386Sopenharmony_ci        double yy = y;
42cb93a386Sopenharmony_ci        double zz = z;
43cb93a386Sopenharmony_ci        return (float)sqrt(xx * xx + yy * yy + zz * zz);
44cb93a386Sopenharmony_ci    }
45cb93a386Sopenharmony_ci}
46cb93a386Sopenharmony_ci
47cb93a386Sopenharmony_ci/*
48cb93a386Sopenharmony_ci *  We have to worry about 2 tricky conditions:
49cb93a386Sopenharmony_ci *  1. underflow of magSq (compared against nearlyzero^2)
50cb93a386Sopenharmony_ci *  2. overflow of magSq (compared w/ isfinite)
51cb93a386Sopenharmony_ci *
52cb93a386Sopenharmony_ci *  If we underflow, we return false. If we overflow, we compute again using
53cb93a386Sopenharmony_ci *  doubles, which is much slower (3x in a desktop test) but will not overflow.
54cb93a386Sopenharmony_ci */
55cb93a386Sopenharmony_cibool SkPoint3::normalize() {
56cb93a386Sopenharmony_ci    float magSq;
57cb93a386Sopenharmony_ci    if (is_length_nearly_zero(fX, fY, fZ, &magSq)) {
58cb93a386Sopenharmony_ci        this->set(0, 0, 0);
59cb93a386Sopenharmony_ci        return false;
60cb93a386Sopenharmony_ci    }
61cb93a386Sopenharmony_ci    // sqrtf does not provide enough precision; since sqrt takes a double,
62cb93a386Sopenharmony_ci    // there's no additional penalty to storing invScale in a double
63cb93a386Sopenharmony_ci    double invScale;
64cb93a386Sopenharmony_ci    if (sk_float_isfinite(magSq)) {
65cb93a386Sopenharmony_ci        invScale = magSq;
66cb93a386Sopenharmony_ci    } else {
67cb93a386Sopenharmony_ci        // our magSq step overflowed to infinity, so use doubles instead.
68cb93a386Sopenharmony_ci        // much slower, but needed when x, y or z is very large, otherwise we
69cb93a386Sopenharmony_ci        // divide by inf. and return (0,0,0) vector.
70cb93a386Sopenharmony_ci        double xx = fX;
71cb93a386Sopenharmony_ci        double yy = fY;
72cb93a386Sopenharmony_ci        double zz = fZ;
73cb93a386Sopenharmony_ci        invScale = xx * xx + yy * yy + zz * zz;
74cb93a386Sopenharmony_ci    }
75cb93a386Sopenharmony_ci    // using a float instead of a double for scale loses too much precision
76cb93a386Sopenharmony_ci    double scale = 1 / sqrt(invScale);
77cb93a386Sopenharmony_ci    fX *= scale;
78cb93a386Sopenharmony_ci    fY *= scale;
79cb93a386Sopenharmony_ci    fZ *= scale;
80cb93a386Sopenharmony_ci    if (!sk_float_isfinite(fX) || !sk_float_isfinite(fY) || !sk_float_isfinite(fZ)) {
81cb93a386Sopenharmony_ci        this->set(0, 0, 0);
82cb93a386Sopenharmony_ci        return false;
83cb93a386Sopenharmony_ci    }
84cb93a386Sopenharmony_ci    return true;
85cb93a386Sopenharmony_ci}
86