<!DOCTYPE html>
<html>
<head>
<title>Approximated curves</title>
<!-- Copyright 1998-2018 by Northwoods Software Corporation. -->
<meta charset="UTF-8">
<script src="go.js"></script>
<script src="../extensions/GeometryReshapingTool.js"></script>
<script id="code">
function lineContainsPoint(ax, ay, bx, by, fuzz, px, py) {
if (fuzz <= 0) fuzz = 0.000001;
var maxx = 0.0;
var minx = 0.0;
var maxy = 0.0;
var miny = 0.0;
if (ax < bx) {
minx = ax;
maxx = bx;
} else {
minx = bx;
maxx = ax;
}
if (ay < by) {
miny = ay;
maxy = by;
} else {
miny = by;
maxy = ay;
}
if (ax === bx) {
return (miny <= py && py <= maxy && ax - fuzz <= px && px <= ax + fuzz);
}
if (ay === by) {
return (minx <= px && px <= maxx && ay - fuzz <= py && py <= ay + fuzz);
}
var xrangeHigh = maxx + fuzz;
var xrangeLow = minx - fuzz;
if ((xrangeLow <= px) && (px <= xrangeHigh)) {
var yrangeHigh = maxy + fuzz;
var yrangeLow = miny - fuzz;
if ((yrangeLow <= py) && (py <= yrangeHigh)) {
if (xrangeHigh - xrangeLow > yrangeHigh - yrangeLow) {
if (ax - bx > fuzz || bx - ax > fuzz) {
var slope = (by - ay) / (bx - ax);
var guessY = (slope * (px - ax) + ay);
if ((guessY - fuzz <= py) && (py <= guessY + fuzz)) {
return true;
}
} else {
return true;
}
} else {
if (ay - by > fuzz || by - ay > fuzz) {
var slope = (bx - ax) / (by - ay);
var guessX = (slope * (py - ay) + ax);
if ((guessX - fuzz <= px) && (px <= guessX + fuzz)) {
return true;
}
} else {
return true;
}
}
}
}
return false;
}
function cubicBezierLines(sx, sy, c1x, c1y, c2x, c2y, ex, ey, epsilon, result) {
if (!lineContainsPoint(sx, sy, ex, ey, epsilon, c1x, c1y) ||
!lineContainsPoint(sx, sy, ex, ey, epsilon, c2x, c2y)) {
var a1x = (sx + c1x) / 2;
var a1y = (sy + c1y) / 2;
var a2x = (c1x + c2x) / 2;
var a2y = (c1y + c2y) / 2;
var a3x = (c2x + ex) / 2;
var a3y = (c2y + ey) / 2;
var vx = (a1x + a2x) / 2;
var vy = (a1y + a2y) / 2;
var wx = (a2x + a3x) / 2;
var wy = (a2y + a3y) / 2;
var mx = (vx + wx) / 2;
var my = (vy + wy) / 2;
cubicBezierLines(sx, sy, a1x, a1y, vx, vy, mx, my, epsilon, result);
cubicBezierLines(mx, my, wx, wy, a3x, a3y, ex, ey, epsilon, result);
} else {
if (result.length === 0) { result.push(sx); result.push(sy); }
result.push(ex); result.push(ey);
}
return result;
}
function quadraticBezierLines(sx, sy, cx, cy, ex, ey, epsilon, result) {
if (!lineContainsPoint(sx, sy, ex, ey, epsilon, cx, cy)) {
var a1x = (sx + cx) / 2;
var a1y = (sy + cy) / 2;
var a2x = (cx + ex) / 2;
var a2y = (cy + ey) / 2;
var mx = (a1x + a2x) / 2;
var my = (a1y + a2y) / 2;
quadraticBezierLines(sx, sy, a1x, a1y, mx, my, epsilon, result);
quadraticBezierLines(mx, my, a2x, a2y, ex, ey, epsilon, result);
} else {
if (result.length === 0) { result.push(sx); result.push(sy); }
result.push(ex); result.push(ey);
}
return result;
}
function init() {
var $ = go.GraphObject.make;
myDiagram = $(go.Diagram, "myDiagramDiv",
{
initialContentAlignment: go.Spot.Center,
initialScale: 2.0,
"ModelChanged": function(e) {
if (e.isTransactionFinished) {
var part = myDiagram.parts.first();
if (!part) {
part = $(go.Part,
{ position: new go.Point(0, 0), layerName: "Grid" },
$(go.Shape, { fill: null, stroke: "red" }));
myDiagram.add(part);
}
var node = myDiagram.nodes.first();
var fig = node.elt(0).geometry.figures.first();
var seg = fig.segments.first();
var lines;
if (seg.type === go.PathSegment.QuadraticBezier) {
lines = quadraticBezierLines(fig.startX, fig.startY, seg.point1X, seg.point1Y, seg.endX, seg.endY, 0.5, []);
} else if (seg.type === go.PathSegment.Bezier) {
lines = cubicBezierLines(fig.startX, fig.startY, seg.point1X, seg.point1Y, seg.point2X, seg.point2Y, seg.endX, seg.endY, 0.5, []);
} else {
return; // only handling first segment being a Bezier curve
}
var newfig = new go.PathFigure(lines[0], lines[1], false, false);
for (var i = 2; i < lines.length; i += 2) {
newfig.add(new go.PathSegment(go.PathSegment.Line, lines[i], lines[i + 1]));
}
part.elt(0).geometry = new go.Geometry().add(newfig);
part.position = node.position;
}
}
});
myDiagram.toolManager.mouseDownTools.insertAt(3, new GeometryReshapingTool());
myDiagram.add(
$(go.Node,
{ position: new go.Point(0, 0), reshapable: true, selectionAdorned: false },
$(go.Shape,
{
name: "SHAPE", // needed by GeometryReshapingTool
fill: "rgba(0,0,0,0.03)",
geometryString: "F1 M0 0 Q50 50 0 100"
//geometryString: "F1 M0 0 C50 0 50 100 0 100"
})
));
myDiagram.select(myDiagram.nodes.first());
}
</script>
</head>
<body onload="init()">
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
</body>
</html>