Commit e83c6683 authored by ed@opera.com's avatar ed@opera.com

[SVG2] Add support for the 'turn' unit in <angle>.

Spec:
https://svgwg.org/svg2-draft/painting.html#OrientAttribute
http://www.w3.org/TR/2012/WD-css3-values-20120308/#angles

BUG=377514

Review URL: https://codereview.chromium.org/303263008

git-svn-id: svn://svn.chromium.org/blink/trunk@176014 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent 16cb2a41
<!doctype html>
<title>SVGAngle tests</title>
<script src=../../resources/testharness.js></script>
<script src=../../resources/testharnessreport.js></script>
<div id="testcontainer">
<svg width="1" height="1" visibility="hidden">
<defs><marker/></defs>
</svg>
</div>
<div id=log></div>
<script>
var svg = document.querySelector("svg");
var marker = document.querySelector("marker");
var EPSILON = Math.pow(2, -8);
var angles = [ 10, 0, 360, 500, 90, 180, 45, 25.9, 145, 270, 0.5, 0.2, 1.37, 3.14159 /* Math.PI */, 0.523599 /* Math.PI/6 */ ];
var units = {
"" : 1,
"deg": 2,
"rad": 3,
"grad": 4,
"turn": 5
};
var highestExposedUnit = 4; // SVG_ANGLETYPE_GRAD
var unitconstants = {
"UNKNOWN" : 0,
"UNSPECIFIED": 1,
"DEG": 2,
"RAD": 3,
"GRAD": 4,
};
var nonexposedunitconstants = {
"TURN": 5
};
function convertTo(value, unit, outunit) {
switch(unit) {
case "":
case "deg":
switch(outunit) {
case "":
case "deg":
return value;
case "rad":
return value*(Math.PI/180);
case "grad":
return value*(400/360);
case "turn":
return value/360;
}
case "rad":
switch(outunit) {
case "":
case "deg":
return value * 180 / Math.PI;
case "rad":
return value;
case "grad":
return value * 180 / Math.PI * 400 / 360;
case "turn":
return value / (2 * Math.PI);
}
case "grad":
switch(outunit) {
case "":
case "deg":
return value * 360 / 400;
case "rad":
return value * Math.PI * 2 / 400;
case "grad":
return value;
case "turn":
return value / 400;
}
case "turn":
switch(outunit) {
case "":
case "deg":
return value * 360;
case "rad":
return value * Math.PI * 2;
case "grad":
return value * 400;
case "turn":
return value;
}
}
}
function createAngle(valuestr) {
var angle = svg.createSVGAngle();
angle.valueAsString = valuestr;
return angle;
}
for(var unit in units) {
test(function() {
var result = undefined;
try {
var a = createAngle(47 + unit);
result = a.unitType;
}
catch(e) {}
if (units[unit] > highestExposedUnit)
assert_equals(result, undefined);
else
assert_equals(result, units[unit]);
}, "SVGAngle(47" + unit + ").unitType");
}
for(var constant in unitconstants) {
var str = "SVG_ANGLETYPE_" + constant;
test(function() {
assert_exists(SVGAngle, str, "");
}, "SVGAngle." + str);
}
for(var constant in nonexposedunitconstants) {
var str = "SVG_ANGLETYPE_" + constant;
test(function() {
assert_not_exists(SVGAngle, str, "");
}, "SVGAngle." + str);
}
angles.forEach(function(angle) {
for(var unit in units) {
var anglestr = angle + unit;
var ref;
try {
ref = createAngle(anglestr);
}
catch(e) {
continue;
}
test(function() {
assert_approx_equals(angle, ref.valueInSpecifiedUnits, EPSILON);
}, "SVGAngle(" + anglestr + ").valueInSpecifiedUnits");
try {
marker.setAttribute("orient", anglestr);
test(function() {
assert_equals(marker.orientAngle.baseVal.valueAsString, anglestr);
}, "orient=\"" + anglestr + "\".valueAsString");
test(function() {
assert_approx_equals(marker.orientAngle.baseVal.value, convertTo(angle, unit, "deg"), EPSILON);
}, "orient=\"" + anglestr + "\".value");
}
finally {
marker.removeAttribute("orient");
}
for (var otherunit in units) {
test(function() {
var a = createAngle(anglestr);
try {
a.convertToSpecifiedUnits(units[otherunit]);
}
catch(e) {}
// unknown unit
if (units[otherunit] > highestExposedUnit)
assert_approx_equals(a.valueInSpecifiedUnits, angle, EPSILON);
else
assert_approx_equals(a.valueInSpecifiedUnits, convertTo(angle, unit, otherunit), EPSILON);
}, "SVGAngle(" + anglestr + ").convertToSpecifiedUnits(" + units[otherunit] + " /*" + (otherunit ? otherunit : "unspecified") + "*/)");
test(function() {
var result = "";
try {
var a = createAngle(47 + otherunit);
a.newValueSpecifiedUnits(units[unit], angle);
result = a.valueAsString;
}
catch(e) {
}
// unknown unit
if (units[unit] > highestExposedUnit || units[otherunit] > highestExposedUnit)
assert_equals(result, "");
else
assert_equals(result, ref.valueAsString);
}, "newValueSpecifiedUnits(" + units[unit] + ", " + angle + ")" );
};
}
});
</script>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="800" height="600">
<defs>
<marker id="turn0.2" refY="5" viewBox="0 0 10 10" orient="72deg">
<path d="M0 0l10 5 -10 5z"/>
</marker>
<marker id="turn0.5" refY="5" viewBox="0 0 10 10" orient="180deg">
<path d="M0 0l10 5 -10 5z"/>
</marker>
<marker id="turn1" refY="5" viewBox="0 0 10 10" orient="360deg">
<path d="M0 0l10 5 -10 5z"/>
</marker>
<marker id="turn1.7" refY="5" viewBox="0 0 10 10" orient="612deg">
<path d="M0 0l10 5 -10 5z"/>
</marker>
<style>
.test {
stroke: black;
stroke-width: 10px;
}
#t1 { marker-end: url(#turn0.2); }
#t2 { marker-end: url(#turn0.5); }
#t3 { marker-end: url(#turn1); }
#t4 { marker-end: url(#turn1.7); }
</style>
</defs>
<path id="t1" class="test" d="M10 40h200" />
<path id="t2" class="test" d="M10 80h200" />
<path id="t3" class="test" d="M10 120h200" />
<path id="t4" class="test" d="M10 160h200" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="800" height="600">
<defs>
<marker id="turn0.2" refY="5" viewBox="0 0 10 10" orient="0.2turn">
<path d="M0 0l10 5 -10 5z"/>
</marker>
<marker id="turn0.5" refY="5" viewBox="0 0 10 10" orient="0.5turn">
<path d="M0 0l10 5 -10 5z"/>
</marker>
<marker id="turn1" refY="5" viewBox="0 0 10 10" orient="1turn">
<path d="M0 0l10 5 -10 5z"/>
</marker>
<marker id="turn1.7" refY="5" viewBox="0 0 10 10" orient="1.7turn">
<path d="M0 0l10 5 -10 5z"/>
</marker>
<style>
.test {
stroke: black;
stroke-width: 10px;
}
#t1 { marker-end: url(#turn0.2); }
#t2 { marker-end: url(#turn0.5); }
#t3 { marker-end: url(#turn1); }
#t4 { marker-end: url(#turn1.7); }
</style>
</defs>
<path id="t1" class="test" d="M10 40h200" />
<path id="t2" class="test" d="M10 80h200" />
<path id="t3" class="test" d="M10 120h200" />
<path id="t4" class="test" d="M10 160h200" />
</svg>
......@@ -117,6 +117,8 @@ float SVGAngle::value() const
return grad2deg(m_valueInSpecifiedUnits);
case SVG_ANGLETYPE_RAD:
return rad2deg(m_valueInSpecifiedUnits);
case SVG_ANGLETYPE_TURN:
return turn2deg(m_valueInSpecifiedUnits);
case SVG_ANGLETYPE_UNSPECIFIED:
case SVG_ANGLETYPE_UNKNOWN:
case SVG_ANGLETYPE_DEG:
......@@ -136,6 +138,9 @@ void SVGAngle::setValue(float value)
case SVG_ANGLETYPE_RAD:
m_valueInSpecifiedUnits = deg2rad(value);
break;
case SVG_ANGLETYPE_TURN:
m_valueInSpecifiedUnits = deg2turn(value);
break;
case SVG_ANGLETYPE_UNSPECIFIED:
case SVG_ANGLETYPE_UNKNOWN:
case SVG_ANGLETYPE_DEG:
......@@ -168,6 +173,8 @@ static SVGAngle::SVGAngleType stringToAngleType(const CharType*& ptr, const Char
const CharType fourthChar = *ptr++;
if (firstChar == 'g' && secondChar == 'r' && thirdChar == 'a' && fourthChar == 'd')
type = SVGAngle::SVG_ANGLETYPE_GRAD;
else if (firstChar == 't' && secondChar == 'u' && thirdChar == 'r' && fourthChar == 'n')
type = SVGAngle::SVG_ANGLETYPE_TURN;
}
}
......@@ -192,6 +199,10 @@ String SVGAngle::valueAsString() const
DEFINE_STATIC_LOCAL(String, gradString, ("grad"));
return String::number(m_valueInSpecifiedUnits) + gradString;
}
case SVG_ANGLETYPE_TURN: {
DEFINE_STATIC_LOCAL(String, turnString, ("turn"));
return String::number(m_valueInSpecifiedUnits) + turnString;
}
case SVG_ANGLETYPE_UNSPECIFIED:
case SVG_ANGLETYPE_UNKNOWN:
return String::number(m_valueInSpecifiedUnits);
......@@ -263,6 +274,24 @@ void SVGAngle::convertToSpecifiedUnits(SVGAngleType unitType, ExceptionState& ex
return;
switch (m_unitType) {
case SVG_ANGLETYPE_TURN:
switch (unitType) {
case SVG_ANGLETYPE_GRAD:
m_valueInSpecifiedUnits = turn2grad(m_valueInSpecifiedUnits);
break;
case SVG_ANGLETYPE_UNSPECIFIED:
case SVG_ANGLETYPE_DEG:
m_valueInSpecifiedUnits = turn2deg(m_valueInSpecifiedUnits);
break;
case SVG_ANGLETYPE_RAD:
m_valueInSpecifiedUnits = deg2rad(turn2deg(m_valueInSpecifiedUnits));
break;
case SVG_ANGLETYPE_TURN:
case SVG_ANGLETYPE_UNKNOWN:
ASSERT_NOT_REACHED();
break;
}
break;
case SVG_ANGLETYPE_RAD:
switch (unitType) {
case SVG_ANGLETYPE_GRAD:
......@@ -272,6 +301,9 @@ void SVGAngle::convertToSpecifiedUnits(SVGAngleType unitType, ExceptionState& ex
case SVG_ANGLETYPE_DEG:
m_valueInSpecifiedUnits = rad2deg(m_valueInSpecifiedUnits);
break;
case SVG_ANGLETYPE_TURN:
m_valueInSpecifiedUnits = deg2turn(rad2deg(m_valueInSpecifiedUnits));
break;
case SVG_ANGLETYPE_RAD:
case SVG_ANGLETYPE_UNKNOWN:
ASSERT_NOT_REACHED();
......@@ -287,6 +319,9 @@ void SVGAngle::convertToSpecifiedUnits(SVGAngleType unitType, ExceptionState& ex
case SVG_ANGLETYPE_DEG:
m_valueInSpecifiedUnits = grad2deg(m_valueInSpecifiedUnits);
break;
case SVG_ANGLETYPE_TURN:
m_valueInSpecifiedUnits = grad2turn(m_valueInSpecifiedUnits);
break;
case SVG_ANGLETYPE_GRAD:
case SVG_ANGLETYPE_UNKNOWN:
ASSERT_NOT_REACHED();
......@@ -303,9 +338,12 @@ void SVGAngle::convertToSpecifiedUnits(SVGAngleType unitType, ExceptionState& ex
case SVG_ANGLETYPE_GRAD:
m_valueInSpecifiedUnits = deg2grad(m_valueInSpecifiedUnits);
break;
case SVG_ANGLETYPE_UNSPECIFIED:
case SVG_ANGLETYPE_TURN:
m_valueInSpecifiedUnits = deg2turn(m_valueInSpecifiedUnits);
break;
case SVG_ANGLETYPE_UNSPECIFIED:
case SVG_ANGLETYPE_DEG:
break;
case SVG_ANGLETYPE_UNKNOWN:
ASSERT_NOT_REACHED();
break;
......
......@@ -67,7 +67,8 @@ public:
SVG_ANGLETYPE_UNSPECIFIED = 1,
SVG_ANGLETYPE_DEG = 2,
SVG_ANGLETYPE_RAD = 3,
SVG_ANGLETYPE_GRAD = 4
SVG_ANGLETYPE_GRAD = 4,
SVG_ANGLETYPE_TURN = 5
};
static PassRefPtr<SVGAngle> create()
......
......@@ -32,6 +32,7 @@
#include "core/svg/SVGAngleTearOff.h"
#include "bindings/v8/ExceptionState.h"
#include "bindings/v8/ExceptionStatePlaceholder.h"
#include "core/dom/ExceptionCode.h"
namespace WebCore {
......@@ -108,7 +109,16 @@ void SVGAngleTearOff::setValueAsString(const String& value, ExceptionState& exce
return;
}
String oldValue = target()->valueAsString();
target()->setValueAsString(value, exceptionState);
if (!exceptionState.hadException() && !hasExposedAngleUnit()) {
target()->setValueAsString(oldValue, ASSERT_NO_EXCEPTION); // rollback to old value
exceptionState.throwDOMException(SyntaxError, "The value provided ('" + value + "') is invalid.");
return;
}
commitChange();
}
......
......@@ -54,7 +54,7 @@ public:
virtual ~SVGAngleTearOff();
unsigned short unitType() { return target()->unitType(); }
unsigned short unitType() { return hasExposedAngleUnit() ? target()->unitType() : SVGAngle::SVG_ANGLETYPE_UNKNOWN; }
void setValue(float, ExceptionState&);
float value() { return target()->value(); }
......@@ -65,11 +65,13 @@ public:
void newValueSpecifiedUnits(unsigned short unitType, float valueInSpecifiedUnits, ExceptionState&);
void convertToSpecifiedUnits(unsigned short unitType, ExceptionState&);
String valueAsString() { return target()->valueAsString(); }
String valueAsString() { return hasExposedAngleUnit() ? target()->valueAsString() : String::number(0); }
void setValueAsString(const String&, ExceptionState&);
private:
SVGAngleTearOff(PassRefPtr<SVGAngle>, SVGElement*, PropertyIsAnimValType, const QualifiedName&);
bool hasExposedAngleUnit() { return target()->unitType() <= SVGAngle::SVG_ANGLETYPE_GRAD; }
};
} // namespace WebCore
......
......@@ -208,6 +208,8 @@ inline double turn2deg(double t) { return t * 360.0; }
inline double deg2turn(double d) { return d / 360.0; }
inline double rad2grad(double r) { return r * 200.0 / piDouble; }
inline double grad2rad(double g) { return g * piDouble / 200.0; }
inline double turn2grad(double t) { return t * 400; }
inline double grad2turn(double g) { return g / 400; }
inline float deg2rad(float d) { return d * piFloat / 180.0f; }
inline float rad2deg(float r) { return r * 180.0f / piFloat; }
......@@ -217,6 +219,8 @@ inline float turn2deg(float t) { return t * 360.0f; }
inline float deg2turn(float d) { return d / 360.0f; }
inline float rad2grad(float r) { return r * 200.0f / piFloat; }
inline float grad2rad(float g) { return g * piFloat / 200.0f; }
inline float turn2grad(float t) { return t * 400; }
inline float grad2turn(float g) { return g / 400; }
// std::numeric_limits<T>::min() returns the smallest positive value for floating point types
template<typename T> inline T defaultMinimumForClamp() { return std::numeric_limits<T>::min(); }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment