/** * Operations have to be done per-channel, if not, * channels will spill onto each other. Once we have * our result, in the form of an integer triplet, * we create a new color node to hold the result. * * @param Context $context * @param string $op * @param Node $other * @return ColorNode * @throws InvalidArgumentException */ public function operate(Context $context, $op, Node $other) { $result = []; if (!$other instanceof ColorNode) { if (!$other instanceof ToColorConvertibleInterface) { throw new InvalidArgumentException('The other node must implement toColor() method to operate, see ILess\\Node\\Node_ToColorConvertibleInterface'); } $other = $other->toColor(); if (!$other instanceof ColorNode) { throw new InvalidArgumentException('The toColor() method must return an instance of ILess\\Node\\Node_Color'); } } $t = $this->getRGB(); $o = $other->getRGB(); for ($c = 0; $c < 3; $c++) { $result[$c] = Math::operate($op, $t[$c], $o[$c]); if ($result[$c] > 255) { $result[$c] = 255; } elseif ($result < 0) { $result[$c] = 0; } } return new ColorNode($result, $this->value->getAlpha() + $other->value->getAlpha()); }
/** * @covers round * @dataProvider getDataForRoundTest */ public function testRound($value, $precision, $expected) { $this->assertEquals($expected, Math::round($value, $precision), sprintf('Rounding of "%s" with precision "%s" works', $value, $precision)); }
/** * Returns the 'value' channel of @color in the HSV space * * @param ColorNode $color * @return string */ public function hsvvalue(Node $color) { if (!$color instanceof ColorNode) { return $color; } $hsv = $color->toHSV(); return new DimensionNode(Math::round($hsv['v'] * 100), '%'); }
/** * Round the value using the `$context->precision` setting. * * @param Context $context * @param mixed $value * * @return string */ public static function round(Context $context, $value) { // add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999....) are properly rounded... return $context->numPrecision === null ? $value : Math::toFixed($value + 2.0E-16, $context->numPrecision); }
/** * Returns the color as HEX string (when transparency present, in RGBA model) * * @param boolean $compress Compress the color? * @param boolean $canShorten Can the color be shortened if possible? * @return string */ public function toString($compress = false, $canShorten = false) { if ($this->isTransparentKeyword) { return 'transparent'; } if ($this->originalForm) { return $this->originalForm; } $alpha = Math::toFixed($this->alpha + 2.0E-16, 8); if ($alpha < 1) { $fixedRGB = $this->getFixedRGB(); return sprintf('rgba(%s)', join($compress ? ',' : ', ', [$fixedRGB[0], $fixedRGB[1], $fixedRGB[2], Math::clean($this->clamp($alpha, 1))])); } // prevent named colors if ($this->keyword) { return $this->keyword; } $color = []; foreach ($this->getFixedRgb() as $i) { $color[] = str_pad(dechex(Math::round($i)), 2, '0', STR_PAD_LEFT); } $color = join('', $color); // convert color to short format if ($canShorten && $color[0] === $color[1] && $color[2] === $color[3] && $color[4] === $color[5]) { $color = $color[0] . $color[2] . $color[4]; } $color = '#' . $color; return $color; }
/** * Operates with the dimension. In an operation between two dimensions, * we default to the first Dimension's unit, * so `1px + 2` will yield `3px`. * * @param Context $context * @param string $op * @param DimensionNode $other * @return DimensionNode * @throws CompilerException */ public function operate(Context $context, $op, DimensionNode $other) { $value = Math::operate($op, $this->value, $other->value); $unit = clone $this->unit; if ($op === '+' || $op === '-') { if (!count($unit->numerator) && !count($unit->denominator)) { $unit = clone $other->unit; if ($this->unit->backupUnit) { $unit->backupUnit = $this->unit->backupUnit; } } elseif (!count($other->unit->numerator) && !count($other->unit->denominator)) { // do nothing } else { $other = $other->convertTo($this->unit->usedUnits()); if ($context->strictUnits && $other->unit->toString() !== $unit->toString()) { throw new CompilerException(sprintf('Incompatible units. Change the units or use the unit function. Bad units: \'%s\' and \'%s\'.', $unit->toString(), $other->unit->toString())); } $value = Math::operate($op, $this->value, $other->value); } } elseif ($op === '*') { $unit->numerator = array_merge($unit->numerator, $other->unit->numerator); $unit->denominator = array_merge($unit->denominator, $other->unit->denominator); sort($unit->numerator); sort($unit->denominator); $unit->cancel(); } elseif ($op === '/') { $unit->numerator = array_merge($unit->numerator, $other->unit->denominator); $unit->denominator = array_merge($unit->denominator, $other->unit->numerator); sort($unit->numerator); sort($unit->denominator); $unit->cancel(); } return new DimensionNode($value, $unit); }