# All files / src/WebGL/core Color.ts

53.33% Statements 40/75
46.15% Branches 30/65
77.77% Functions 7/9
55.07% Lines 38/69

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150

    267x
    267x
    267x

    267x
    238x
    238x
    238x

    238x

    29x

    29x
    28x
    2x
    2x
    2x
    2x

    28x

    27x

    27x
    27x
    27x

    27x

    3x
    3x
    3x
    3x

    2x

    1x
    1x
    1x

    1x

    1x
    1x
    1x
    1x
    1x
    1x

    18x

    18x

     export interface Colored {
      r: number;
      g: number;
      b: number;
      a?: number;
    }

    export type ColorConstructorArg = number | Color | Colored | undefined;
    export class Color implements Colored {
      r: number = 0.0;
      g: number = 0.0;
      b: number = 0.0;

      constructor(r?: ColorConstructorArg, g?: number, b?: number) {
        if (arguments.length > 1 && typeof r === "number") {
          this.r = r || 0.0;
          this.g = g || 0.0;
          this.b = b || 0.0;

          return this;
        }

        return this.set(r || 0.0);
      }

      set<T extends Colored>(val: Color | number | T): Color {
        if (val instanceof Color) return val.clone();
        else if (typeof val === "number") this.setHex(val);
        else if (typeof val === "object") {
          this.r = val?.r || 0.0;
          this.g = val?.g || 0.0;
          this.b = val?.b || 0.0;
        }
        return this;
      }

      setHex(hex: number): Color {
        hex = Math.floor(hex);

        this.r = ((hex >> 16) & 255) / 255;
        this.g = ((hex >> 8) & 255) / 255;
        this.b = (hex & 255) / 255;

        return this;
      }

      getHex(): number {
        var R = Math.round(this.r * 255);
        var G = Math.round(this.g * 255);
        var B = Math.round(this.b * 255);
        return (R << 16) | (G << 8) | B;
      }

      clone(): Color {
        return new Color(this.r, this.g, this.b);
      }

      copy(color: Color): Color {
        this.r = color.r;
        this.g = color.g;
        this.b = color.b;

        return this;
      }

      //return object that represents color components from 0 to 255
      scaled<T extends Colored>(): Colored {
        var ret: Partial<T> = {};
        ret.r = Math.round(this.r * 255);
        ret.g = Math.round(this.g * 255);
        ret.b = Math.round(this.b * 255);
        ret.a = 1.0;
        return ret as T;
      }
    }

    // in an attempt to reduce memory overhead, cache all $3Dmol.Colors
    // this makes things a little faster
    export class CC {
      static rgbRegEx =
        /rgb(a?)\(\s*([^ ,\)\t]+)\s*,\s*([^ ,\)\t]+)\s*,\s*([^ ,\)\t]+)/i;
      static cache: Record<number, Color> = { 0: new Color(0) };
      static color(hex: undefined): Color;
      static color(hex: number): Color;
      static color(hex: number[]): Color[];
      static color(hex?: number | number[]) {
        // Undefined values default to black
        Iif (!hex) return CC.cache[0];
        // cache hits
        Iif (typeof hex === "number" && typeof CC.cache[hex] !== "undefined")
          return CC.cache[hex];

        // arrays
        Iif (hex && Array.isArray(hex))
          // parse elements recursively
          return hex.map(CC.color as (hex: number) => Color);

        // numbers and hex strings
        // @ts-ignore
        hex = CC.getHex(hex);
        Iif (typeof hex === "number") {
          var c = new Color(hex);
          CC.cache[hex] = c;
          return c;
        }
        // pass through $3Dmol.Color & other objects
        return hex;
      }

      static getHex(hex: Array<string|number>): number[];
      static getHex(hex: string | number): number;
      static getHex(hex) {
        Iif (Array.isArray(hex)) return hex.map(CC.getHex) as number[];
        Iif (typeof hex === "string") {
          Iif (!isNaN(parseInt(hex))) return parseInt(hex);

          hex = hex.trim();

          Iif (hex.length == 4 && hex[0] == "#") {
            hex = "#" + hex[1] + hex[1] + hex[2] + hex[2] + hex[3] + hex[3]; //expand to full hex number
          }

          Iif (hex.length == 7 && hex[0] == "#") {
            return parseInt(hex.substring(1), 16);
          }

          let m = CC.rgbRegEx.exec(hex);
          Iif (m) {
            Iif (m[1] != "") {
              console.log(
                "WARNING: Opacity value in rgba ignored.  Specify separately as opacity attribute."
              );
            }
            let ret = 0;
            for (let i = 2; i < 5; i++) {
              ret *= 256;
              // @ts-ignore
              let val = m[i].endsWith("%")
                ? (255 * parseFloat(m[i])) / 100
                : parseFloat(m[i]);
              ret += Math.round(val);
            }
            return ret;
          }
          return (window as any)?.$3Dmol?.htmlColors[hex.toLowerCase()] || 0x000000;
        }
        return hex;
      }
    }