<html>
<head>
<script src="http://code.jquery.com/jquery-1.8.3.min.js"></script>
<style>
rect{
/*shape-rendering:crispEdges*/
}
path {
stroke-opacity:1.0;
stroke-width:1;
stroke:black;
}
.newpath {
stroke-width:3;
stroke:#00FF00;
stroke-opacity:0.5;
shape-rendering:crispEdges
}
svg {border:1px solid red}
#containerDiv{
width:100%;
height:100%;
/*overflow:scroll;*/
}
#coord{white-space:nowrap}
body {height:11000px}
</style>
<script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
</head>
<meta charset=utf-8 />
<title>Bounding box, various cubic curves</title>
</head>
<body>
<h2>Bounding box, various cubic curves</h2>
<div id="coord"></div>
<div id="containerDiv"></div>
</body>
</html>
// noprotect
var data1 = ["M352,5779.724c499,2-3-89,496-84",
"M478,2877.472c247,3-255-88,244-83",
"M478,2392.817c247,3-246,14,244-83",
"M478,3361.031c247-69-246,14,244-83",
"M500.5,1403.542c273-45-220,38,199-37",
"M500.5,2587.469c215,96-220,38,199-37",
"M406,1823.559c404,146-31,88,388,13",
"M406,2064.553c86,150-31,88,388,13",
"M406,4966.489c261,149-31,88,388,13",
"M406,4484.496c382,141-31,88,388,13",
"M394.5,6471.436c405,75-8,22,411-53",
"M394.5,5279.895c449-4-8,22,411-53",
"M394.5,6247.762c449-4,0,0,411-53",
"M489.5,3565.342c449-4,0,0,221-8",
"M479.072,3809.049c529-26,0,0,167-6",
"M516.5,1644.744c352-80,0,0,167-6",
"M798.002,4082.546c-529,7-523-141.999-12-38.999",
"M801.469,6774.896c-550.999-16-522.999-142-1-140",
"M799.765,4354.818c-542,32-523-142-1-140",
"M770.439,7017.016c-380.001-148-523.001-142-1-140",
"M769.988,274.009C390.988,266.009,247.988,272.009,769.988,274.009",
"M794.813,1141.56C277.813,1150.56,272.813,1139.56,794.813,1141.56",
"M785.217,901.998C320.216,900.998,263.217,899.998,785.217,901.998",
"M347.5,462.172c505-17-17-18,505-16",
"M339.5,663.235c521,0-1-1,521,1",
"M377.5,5495.454c445,0-77-1,445,1",
"M377.5,5978.951c372,1-77-1,445,1",
"M564.756,3078.079c372,1-77-1-77-1",
"M602.874,4750.803c196.999,91-77-1-77-1",
"M731.169,7500.317c-551-16-74.463,21-1-140",
"M731.169,7209.412c-551-4.777-74.463,6.271-1-41.811",
"M1075.169,7682.533c-1996.043-2.379-269.747,3.121-3.622-20.811",
"M612.657,8228.399c-57.988-464.354,2.249,314.834-1-140",
"M748.791,9246.203c-48.561-445.45,0,0,0-246.418",
"M407.051,9488.014c8.771-403.029,0,0,0-246.416",
"M492,7954.201c258.471-128.029-126-68.002,216-56",
"M529.006,8937.162c232.271-130.145,105.271-167.076,123.23-0.48",
"M532.461,8453.83c213.563-131.8,105.271-167.076,123.232-0.479",
"M538.385,8695.068c169.485-128.53,105.27-167.075,123.23-0.479",
"M177.365,9657.754 C2314.484,9433.758-164.635,9645.75,177.365,9657.754",
"M177.365,9863.064 C2314.484,9798.949-164.635,9859.629,177.365,9863.064",
"M177.365,10098.412 C2314.482,10062.607-164.635,10096.492,177.365,10098.412",
"M121.108,10352.402c2137.118-35.807,82,41-10.136-40.705",
"M163.396,10885.141 C453.589,10902.539,162.471,10562.539,163.396,10885.141",
"M228.071,10627.99 C1915.305,10622.137,202.129,10387.359,228.071,10627.99",
"M152.222,10991.809 C1084.453,10959.602,1582.453,10868.578,152.222,10991.809"];
(function(){
// ------------------------------------------------
var pow = Math.pow,
sqrt = Math.sqrt,
min = Math.min,
max = Math.max;
abs = Math.abs;
function getBoundsOfCurve (x0, y0, x1, y1, x2, y2, x3, y3)
{
var tvalues = new Array();
var bounds = [new Array(), new Array()];
var points = new Array();
var a,b,c,t,t1,t2,b2ac,sqrtb2ac;
for (var i = 0; i < 2; ++i)
{
if (i==0)
{
b = 6 * x0 - 12 * x1 + 6 * x2;
a = -3 * x0 + 9 * x1 - 9 * x2 + 3 * x3;
c = 3 * x1 - 3 * x0;
}
else
{
b = 6 * y0 - 12 * y1 + 6 * y2;
a = -3 * y0 + 9 * y1 - 9 * y2 + 3 * y3;
c = 3 * y1 - 3 * y0;
}
if (abs(a) < 1e-12) // Numerical robustness
{
if (abs(b) < 1e-12) // Numerical robustness
{
continue;
}
t = -c / b;
if (0 < t && t < 1)
{
tvalues.push(t);
}
continue;
}
b2ac = b*b - 4 * c * a;
sqrtb2ac = sqrt(b2ac);
if (b2ac < 0)
{
continue;
}
t1 = (-b + sqrtb2ac) / (2 * a);
if (0 < t1 && t1 < 1)
{
tvalues.push(t1);
}
t2 = (-b - sqrtb2ac) / (2 * a);
if (0 < t2 && t2 < 1)
{
tvalues.push(t2);
}
}
var x, y, j = tvalues.length, jlen = j, mt;
while(j--)
{
t = tvalues[j];
mt = 1-t;
x = (mt*mt*mt*x0) + (3*mt*mt*t*x1) + (3*mt*t*t*x2) + (t*t*t*x3);
bounds[0][j] = x;
y = (mt*mt*mt*y0) + (3*mt*mt*t*y1) + (3*mt*t*t*y2) + (t*t*t*y3);
bounds[1][j] = y;
points[j] = {X: x, Y: y};
}
tvalues[jlen] = 0;
tvalues[jlen+1] = 1;
points[jlen] = {X: x0, Y:y0};
points[jlen+1] = {X: x3, Y:y3};
bounds[0][jlen] = x0;
bounds[1][jlen] = y0;
bounds[0][jlen+1] = x3;
bounds[1][jlen+1] = y3;
tvalues.length = bounds[0].length = bounds[1].length = points.length = jlen+2;
return {
left: min.apply(null, bounds[0]),
top: min.apply(null, bounds[1]),
right: max.apply(null, bounds[0]),
bottom: max.apply(null, bounds[1]),
points: points, // local extremes
tvalues: tvalues // t values of local extremes
};
};
function getBoundsOfPath (path)
{
//var curve = path2curve(path);
curve = path;
////console.log(JSON.stringify(path));
////console.log("--------------------------------------------------------------------");
//console.log(JSON.stringify(curve));
var bounds, s, startX, startY,
minx = Number.MAX_VALUE,
miny = Number.MAX_VALUE,
maxx = Number.MIN_VALUE,
maxy = Number.MIN_VALUE;
var isC = false;
for (var i = 0, ilen = curve.length; i < ilen; i++)
{
//var val = 6;
//if(i!=val && i!=val-1) continue;
var s = curve[i];
////console.log(s);
if (s[0] == 'M')
{
if (typeof(curve[i+1]) != "undefined" && curve[i+1][0] == "C")
{
startX = s[1];
startY = s[2];
if (startX < minx) minx = startX;
if (startX > maxx) maxx = startX;
if (startY < miny) miny = startY;
if (startY > maxy) maxy = startY;
}
}
else
if (s[0] == 'C')
{
isC = true;
//if(i==val)
//{
bounds = getBoundsOfCurve(startX, startY, s[1], s[2], s[3], s[4], s[5], s[6]);
//bounds = calculate_standard_bbox(startX, startY, s[1], s[2], s[3], s[4], s[5], s[6]);
//bounds = cubic_extrema_external(startX, startY, s[1], s[2], s[3], s[4], s[5], s[6]);
////console.log(JSON.stringify(bounds));
if (bounds.left < minx) minx = bounds.left;
if (bounds.right > maxx) maxx = bounds.right;
if (bounds.top < miny) miny = bounds.top;
if (bounds.bottom > maxy) maxy = bounds.bottom;
startX = s[5];
startY = s[6];
}
////console.log(JSON.stringify(bounds));
//if(s[0] == 'C')
//{
// currentX = s[5], currentY = s[6];
//}
}
// left: this.left + (minX + deltaX / 2),
// top: this.top + (minY + deltaY / 2),
if (!isC) minx = maxx = miny = maxy = 0;
var boundsFinal = {
left: minx,
top: miny,
width: maxx - minx,
height: maxy - miny
};
////console.log(JSON.stringify(boundsFinal));
return boundsFinal;
}
window.getBoundsOfPath = getBoundsOfPath;
window.getBoundsOfCurve = getBoundsOfCurve;
})();
// DRAW SECTION
function original_data_arr_to_path (arr)
{
// M366,75C59,40,59,40,366,75
var str="M"+arr[0]+","+arr[1];
str+="C"+arr[2]+",";
str+=arr[3]+",";
str+=arr[4]+",";
str+=arr[5]+",";
str+=arr[6]+",";
str+=arr[7]+",";
return str;
}
function newdata_arr_to_path (arr)
{
var str="";
for(var i=0,m=arr.length;i<m;i++)
{
str+=i?"L":"M";
str+=arr[i].X+","+arr[i].Y;
}
return str;
}
var w = 1200, h=11200;
var R = Raphael("containerDiv",w,h);
R.canvas.setAttribute("id", "p");
//Raphael.parsePathString("M193,169C200,5,200,210,26,74Z");
var d;
var b; // bounds
var p; // path
for(i=0;i<data1.length;i++)
{
d = data1[i];
d = Raphael.parsePathString(d);
d = Raphael._pathToAbsolute(d);
p = d;
b = getBoundsOfCurve(
p[0][1],
p[0][2],
p[1][1],
p[1][2],
p[1][3],
p[1][4],
p[1][5],
p[1][6]);
R.rect(b.left,b.top,b.right-b.left,b.bottom-b.top).attr("stroke", "red").attr("fill","red").attr("stroke-opacity",0.5).attr("fill-opacity",0.1);
R.path(d);
for(j=0;j<b.points.length;j++)
{
R.circle(b.points[j].X,b.points[j].Y, 5).attr("stroke", "red").attr("fill","red").attr("stroke-opacity",0.5).attr("fill-opacity",0.1);
}
}
Output
300px
You can jump to the latest bin by adding /latest
to your URL
Keyboard Shortcuts
Shortcut | Action |
---|---|
ctrl + [num] | Toggle nth panel |
ctrl + 0 | Close focused panel |
ctrl + enter | Re-render output. If console visible: run JS in console |
Ctrl + l | Clear the console |
ctrl + / | Toggle comment on selected lines |
ctrl + ] | Indents selected lines |
ctrl + [ | Unindents selected lines |
tab | Code complete & Emmet expand |
ctrl + shift + L | Beautify code in active panel |
ctrl + s | Save & lock current Bin from further changes |
ctrl + shift + s | Open the share options |
ctrl + y | Archive Bin |
Complete list of JS Bin shortcuts |
JS Bin URLs
URL | Action |
---|---|
/ | Show the full rendered output. This content will update in real time as it's updated from the /edit url. |
/edit | Edit the current bin |
/watch | Follow a Code Casting session |
/embed | Create an embeddable version of the bin |
/latest | Load the very latest bin (/latest goes in place of the revision) |
/[username]/last | View the last edited bin for this user |
/[username]/last/edit | Edit the last edited bin for this user |
/[username]/last/watch | Follow the Code Casting session for the latest bin for this user |
/quiet | Remove analytics and edit button from rendered output |
.js | Load only the JavaScript for a bin |
.css | Load only the CSS for a bin |
Except for username prefixed urls, the url may start with http://jsbin.com/abc and the url fragments can be added to the url to view it differently. |