void SkMatrix3D::setRotateZ(SkScalar degZ) { SkScalar s, c; s = SkScalarSinCos(SkDegreesToRadians(degZ), &c); this->setRow(0, c, -s, 0); this->setRow(1, s, c, 0); this->setRow(2, 0, 0, SK_Scalar1); }
void SkColorMatrix::setRotate(Axis axis, SkScalar degrees) { SkScalar S, C; S = SkScalarSinCos(SkDegreesToRadians(degrees), &C); this->setSinCos(axis, S, C); }
void SkMatrix3D::setRotateY(SkScalar degY) { SkScalar s, c; s = SkScalarSinCos(SkDegreesToRadians(degY), &c); this->setRow(0, c, 0, -s); this->setRow(1, 0, SK_Scalar1, 0); this->setRow(2, s, 0, c); }
static void create_ngon(int n, SkPoint* pts, SkScalar width, SkScalar height) { float angleStep = 360.0f / n, angle = 0.0f, sin, cos; if ((n % 2) == 1) { angle = angleStep/2.0f; } for (int i = 0; i < n; ++i) { sin = SkScalarSinCos(SkDegreesToRadians(angle), &cos); pts[i].fX = -sin * width; pts[i].fY = cos * height; angle += angleStep; } }
static void test_matrix_decomposition(skiatest::Reporter* reporter) { SkMatrix mat; SkScalar rotation0, scaleX, scaleY, rotation1; const float kRotation0 = 15.5f; const float kRotation1 = -50.f; const float kScale0 = 5000.f; const float kScale1 = 0.001f; // identity mat.reset(); REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1)); REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation0)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, SK_Scalar1)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, SK_Scalar1)); REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1)); // make sure it doesn't crash if we pass in NULLs REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, NULL, NULL, NULL, NULL)); // rotation only mat.setRotate(kRotation0); REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation0, SkDegreesToRadians(kRotation0))); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, SK_Scalar1)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, SK_Scalar1)); REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1)); // uniform scale only mat.setScale(kScale0, kScale0); REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1)); REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation0)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale0)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, kScale0)); REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1)); // anisotropic scale only mat.setScale(kScale1, kScale0); REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1)); REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation0)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale1)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, kScale0)); REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1)); // rotation then uniform scale mat.setRotate(kRotation1); mat.postScale(kScale0, kScale0); REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation0, SkDegreesToRadians(kRotation1))); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale0)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, kScale0)); REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1)); // uniform scale then rotation mat.setScale(kScale0, kScale0); mat.postRotate(kRotation1); REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation0, SkDegreesToRadians(kRotation1))); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale0)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, kScale0)); REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1)); // rotation then uniform scale+reflection mat.setRotate(kRotation0); mat.postScale(kScale1, -kScale1); REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation0, SkDegreesToRadians(kRotation0))); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale1)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, -kScale1)); REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1)); // uniform scale+reflection, then rotate mat.setScale(kScale0, -kScale0); mat.postRotate(kRotation1); REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation0, SkDegreesToRadians(-kRotation1))); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale0)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, -kScale0)); REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1)); // rotation then anisotropic scale mat.setRotate(kRotation1); mat.postScale(kScale1, kScale0); REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation0, SkDegreesToRadians(kRotation1))); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale1)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, kScale0)); REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1)); // anisotropic scale then rotation mat.setScale(kScale1, kScale0); mat.postRotate(kRotation0); REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1)); REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation0)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale1)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, kScale0)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation1, SkDegreesToRadians(kRotation0))); // rotation, uniform scale, then different rotation mat.setRotate(kRotation1); mat.postScale(kScale0, kScale0); mat.postRotate(kRotation0); REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rotation0, SkDegreesToRadians(kRotation0 + kRotation1))); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleX, kScale0)); REPORTER_ASSERT(reporter, SkScalarNearlyEqual(scaleY, kScale0)); REPORTER_ASSERT(reporter, SkScalarNearlyZero(rotation1)); // rotation, anisotropic scale, then different rotation mat.setRotate(kRotation0); mat.postScale(kScale1, kScale0); mat.postRotate(kRotation1); REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1)); // Because of the shear/skew we won't get the same results, so we need to multiply it out. // Generating the matrices requires doing a radian-to-degree calculation, then degree-to-radian // calculation (in setRotate()), which adds error, so this just computes the matrix elements // directly. SkScalar c0; SkScalar s0 = SkScalarSinCos(rotation0, &c0); SkScalar c1; SkScalar s1 = SkScalarSinCos(rotation1, &c1); // We do a relative check here because large scale factors cause problems with an absolute check REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScaleX], scaleX*c0*c1 - scaleY*s0*s1)); REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX], -scaleX*s0*c1 - scaleY*c0*s1)); REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewY], scaleX*c0*s1 + scaleY*s0*c1)); REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScaleY], -scaleX*s0*s1 + scaleY*c0*c1)); // try some random matrices SkMWCRandom rand; for (int m = 0; m < 1000; ++m) { SkScalar rot0 = rand.nextRangeF(-SK_ScalarPI, SK_ScalarPI); SkScalar sx = rand.nextRangeF(-3000.f, 3000.f); SkScalar sy = rand.nextRangeF(-3000.f, 3000.f); SkScalar rot1 = rand.nextRangeF(-SK_ScalarPI, SK_ScalarPI); mat.setRotate(rot0); mat.postScale(sx, sy); mat.postRotate(rot1); if (SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1)) { SkScalar c0; SkScalar s0 = SkScalarSinCos(rotation0, &c0); SkScalar c1; SkScalar s1 = SkScalarSinCos(rotation1, &c1); REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScaleX], scaleX*c0*c1 - scaleY*s0*s1)); REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX], -scaleX*s0*c1 - scaleY*c0*s1)); REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewY], scaleX*c0*s1 + scaleY*s0*c1)); REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScaleY], -scaleX*s0*s1 + scaleY*c0*c1)); } else { // if the matrix is degenerate, the basis vectors should be near-parallel or near-zero SkScalar perpdot = mat[SkMatrix::kMScaleX]*mat[SkMatrix::kMScaleY] - mat[SkMatrix::kMSkewX]*mat[SkMatrix::kMSkewY]; REPORTER_ASSERT(reporter, SkScalarNearlyZero(perpdot)); } } // translation shouldn't affect this mat.postTranslate(-1000.f, 1000.f); REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1)); s0 = SkScalarSinCos(rotation0, &c0); s1 = SkScalarSinCos(rotation1, &c1); REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScaleX], scaleX*c0*c1 - scaleY*s0*s1)); REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX], -scaleX*s0*c1 - scaleY*c0*s1)); REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewY], scaleX*c0*s1 + scaleY*s0*c1)); REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScaleY], -scaleX*s0*s1 + scaleY*c0*c1)); // perspective shouldn't affect this mat[SkMatrix::kMPersp0] = 12.f; mat[SkMatrix::kMPersp1] = 4.f; mat[SkMatrix::kMPersp2] = 1872.f; REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1)); s0 = SkScalarSinCos(rotation0, &c0); s1 = SkScalarSinCos(rotation1, &c1); REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScaleX], scaleX*c0*c1 - scaleY*s0*s1)); REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX], -scaleX*s0*c1 - scaleY*c0*s1)); REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewY], scaleX*c0*s1 + scaleY*s0*c1)); REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScaleY], -scaleX*s0*s1 + scaleY*c0*c1)); // rotation, anisotropic scale + reflection, then different rotation mat.setRotate(kRotation0); mat.postScale(-kScale1, kScale0); mat.postRotate(kRotation1); REPORTER_ASSERT(reporter, SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1)); s0 = SkScalarSinCos(rotation0, &c0); s1 = SkScalarSinCos(rotation1, &c1); REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScaleX], scaleX*c0*c1 - scaleY*s0*s1)); REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewX], -scaleX*s0*c1 - scaleY*c0*s1)); REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMSkewY], scaleX*c0*s1 + scaleY*s0*c1)); REPORTER_ASSERT(reporter, scalar_nearly_equal_relative(mat[SkMatrix::kMScaleY], -scaleX*s0*s1 + scaleY*c0*c1)); // degenerate matrices // mostly zero entries mat.reset(); mat[SkMatrix::kMScaleX] = 0.f; REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1)); mat.reset(); mat[SkMatrix::kMScaleY] = 0.f; REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1)); mat.reset(); // linearly dependent entries mat[SkMatrix::kMScaleX] = 1.f; mat[SkMatrix::kMSkewX] = 2.f; mat[SkMatrix::kMSkewY] = 4.f; mat[SkMatrix::kMScaleY] = 8.f; REPORTER_ASSERT(reporter, !SkDecomposeUpper2x2(mat, &rotation0, &scaleX, &scaleY, &rotation1)); }
static SkPoint unit_vec(int degrees) { SkScalar rad = SkDegreesToRadians(SkIntToScalar(degrees)); SkScalar s, c; s = SkScalarSinCos(rad, &c); return SkPoint::Make(c, s); }
virtual void onDraw(SkCanvas* canvas) { if (!fInitialized) { make_bitmap(); fInitialized = true; } canvas->clear(0xFF101010); SkPaint checkPaint; checkPaint.setColor(0xFF202020); for (int y = 0; y < HEIGHT; y += 16) { for (int x = 0; x < WIDTH; x += 16) { canvas->save(); canvas->translate(SkIntToScalar(x), SkIntToScalar(y)); canvas->drawRect(SkRect::MakeXYWH(8, 0, 8, 8), checkPaint); canvas->drawRect(SkRect::MakeXYWH(0, 8, 8, 8), checkPaint); canvas->restore(); } } SkPoint3 pointLocation(0, 0, SkIntToScalar(10)); SkScalar azimuthRad = SkDegreesToRadians(SkIntToScalar(225)); SkScalar elevationRad = SkDegreesToRadians(SkIntToScalar(5)); SkPoint3 distantDirection(SkScalarMul(SkScalarCos(azimuthRad), SkScalarCos(elevationRad)), SkScalarMul(SkScalarSin(azimuthRad), SkScalarCos(elevationRad)), SkScalarSin(elevationRad)); SkPoint3 spotLocation(SkIntToScalar(-10), SkIntToScalar(-10), SkIntToScalar(20)); SkPoint3 spotTarget(SkIntToScalar(40), SkIntToScalar(40), 0); SkScalar spotExponent = SK_Scalar1; SkScalar cutoffAngle = SkIntToScalar(15); SkScalar kd = SkIntToScalar(2); SkScalar ks = SkIntToScalar(1); SkScalar shininess = SkIntToScalar(8); SkScalar surfaceScale = SkIntToScalar(1); SkColor white(0xFFFFFFFF); SkPaint paint; SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(20, 10, 60, 65)); int y = 0; for (int i = 0; i < 2; i++) { const SkImageFilter::CropRect* cr = (i == 0) ? NULL : &cropRect; paint.setImageFilter(SkLightingImageFilter::CreatePointLitDiffuse(pointLocation, white, surfaceScale, kd, NULL, cr))->unref(); drawClippedBitmap(canvas, paint, 0, y); paint.setImageFilter(SkLightingImageFilter::CreateDistantLitDiffuse(distantDirection, white, surfaceScale, kd, NULL, cr))->unref(); drawClippedBitmap(canvas, paint, 110, y); paint.setImageFilter(SkLightingImageFilter::CreateSpotLitDiffuse(spotLocation, spotTarget, spotExponent, cutoffAngle, white, surfaceScale, kd, NULL, cr))->unref(); drawClippedBitmap(canvas, paint, 220, y); y += 110; paint.setImageFilter(SkLightingImageFilter::CreatePointLitSpecular(pointLocation, white, surfaceScale, ks, shininess, NULL, cr))->unref(); drawClippedBitmap(canvas, paint, 0, y); paint.setImageFilter(SkLightingImageFilter::CreateDistantLitSpecular(distantDirection, white, surfaceScale, ks, shininess, NULL, cr))->unref(); drawClippedBitmap(canvas, paint, 110, y); paint.setImageFilter(SkLightingImageFilter::CreateSpotLitSpecular(spotLocation, spotTarget, spotExponent, cutoffAngle, white, surfaceScale, ks, shininess, NULL, cr))->unref(); drawClippedBitmap(canvas, paint, 220, y); y += 110; } }
void onOnceBeforeDraw() override { { SkPath* lineAnglesPath = &fPaths.push_back(); enum { kNumAngles = 15, kRadius = 40, }; for (int i = 0; i < kNumAngles; ++i) { SkScalar angle = SK_ScalarPI * SkIntToScalar(i) / kNumAngles; SkScalar x = kRadius * SkScalarCos(angle); SkScalar y = kRadius * SkScalarSin(angle); lineAnglesPath->moveTo(x, y); lineAnglesPath->lineTo(-x, -y); } } { SkPath* kindaTightQuad = &fPaths.push_back(); kindaTightQuad->moveTo(0, -10 * SK_Scalar1); kindaTightQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), -10 * SK_Scalar1, 0); } { SkPath* tightQuad = &fPaths.push_back(); tightQuad->moveTo(0, -5 * SK_Scalar1); tightQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), -5 * SK_Scalar1, 0); } { SkPath* tighterQuad = &fPaths.push_back(); tighterQuad->moveTo(0, -2 * SK_Scalar1); tighterQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), -2 * SK_Scalar1, 0); } { SkPath* unevenTighterQuad = &fPaths.push_back(); unevenTighterQuad->moveTo(0, -1 * SK_Scalar1); SkPoint p; p.set(-2 * SK_Scalar1 + 3 * SkIntToScalar(102) / 4, SkIntToScalar(75)); unevenTighterQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), p.fX, p.fY); } { SkPath* reallyTightQuad = &fPaths.push_back(); reallyTightQuad->moveTo(0, -1 * SK_Scalar1); reallyTightQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), -1 * SK_Scalar1, 0); } { SkPath* closedQuad = &fPaths.push_back(); closedQuad->moveTo(0, -0); closedQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), 0, 0); } { SkPath* unevenClosedQuad = &fPaths.push_back(); unevenClosedQuad->moveTo(0, -0); unevenClosedQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), SkIntToScalar(75), SkIntToScalar(75)); } // Two problem cases for gpu hairline renderer found by shapeops testing. These used // to assert that the computed bounding box didn't contain all the vertices. { SkPath* problem1 = &fPaths.push_back(); problem1->moveTo(SkIntToScalar(4), SkIntToScalar(6)); problem1->cubicTo(SkIntToScalar(5), SkIntToScalar(6), SkIntToScalar(5), SkIntToScalar(4), SkIntToScalar(4), SkIntToScalar(0)); problem1->close(); } { SkPath* problem2 = &fPaths.push_back(); problem2->moveTo(SkIntToScalar(5), SkIntToScalar(1)); problem2->lineTo(4.32787323f, 1.67212653f); problem2->cubicTo(2.75223875f, 3.24776125f, 3.00581908f, 4.51236057f, 3.7580452f, 4.37367964f); problem2->cubicTo(4.66472578f, 3.888381f, 5.f, 2.875f, 5.f, 1.f); problem2->close(); } // Three paths that show the same bug (missing end caps) { // A caret (crbug.com/131770) SkPath* bug0 = &fPaths.push_back(); bug0->moveTo(6.5f,5.5f); bug0->lineTo(3.5f,0.5f); bug0->moveTo(0.5f,5.5f); bug0->lineTo(3.5f,0.5f); } { // An X (crbug.com/137317) SkPath* bug1 = &fPaths.push_back(); bug1->moveTo(1, 1); bug1->lineTo(6, 6); bug1->moveTo(1, 6); bug1->lineTo(6, 1); } { // A right angle (crbug.com/137465 and crbug.com/256776) SkPath* bug2 = &fPaths.push_back(); bug2->moveTo(5.5f, 5.5f); bug2->lineTo(5.5f, 0.5f); bug2->lineTo(0.5f, 0.5f); } { // Arc example to test imperfect truncation bug (crbug.com/295626) static const SkScalar kRad = SkIntToScalar(2000); static const SkScalar kStartAngle = 262.59717f; static const SkScalar kSweepAngle = SkScalarHalf(17.188717f); SkPath* bug = &fPaths.push_back(); // Add a circular arc SkRect circle = SkRect::MakeLTRB(-kRad, -kRad, kRad, kRad); bug->addArc(circle, kStartAngle, kSweepAngle); // Now add the chord that should cap the circular arc SkScalar cosV, sinV = SkScalarSinCos(SkDegreesToRadians(kStartAngle), &cosV); SkPoint p0 = SkPoint::Make(kRad * cosV, kRad * sinV); sinV = SkScalarSinCos(SkDegreesToRadians(kStartAngle + kSweepAngle), &cosV); SkPoint p1 = SkPoint::Make(kRad * cosV, kRad * sinV); bug->moveTo(p0); bug->lineTo(p1); } }
static SkPoint unit_vec(int degrees) { SkScalar rad = SkDegreesToRadians(SkIntToScalar(degrees)); return SkPoint::Make(SkScalarCos(rad), SkScalarSin(rad)); }