Skip to content

Commit

Permalink
LE: Beziers not yet implemented on Web Server
Browse files Browse the repository at this point in the history
Implemented!
  • Loading branch information
geowar1 committed Jul 28, 2018
1 parent f4fa8f7 commit c6b1b0c
Show file tree
Hide file tree
Showing 2 changed files with 170 additions and 20 deletions.
2 changes: 1 addition & 1 deletion java/src/jmri/util/MathUtil.java
Expand Up @@ -1154,7 +1154,7 @@ public static double drawBezier(
GeneralPath path = new GeneralPath();
bezier1st = true;
if (p.length == 4) { // draw cubic bezier?
result = MathUtil.plotBezier(path, p[0], p[1], p[2], p[3], 0, displacement);
result = plotBezier(path, p[0], p[1], p[2], p[3], 0, displacement);
} else { // (nope)
result = plotBezier(path, p, 0, displacement);
}
Expand Down
188 changes: 169 additions & 19 deletions web/js/panel.js
Expand Up @@ -100,14 +100,16 @@ var jmri_logging = false;
//

// log object properties
function $logProperties(obj) {
var $propList = "";
for (var $propName in obj) {
if (typeof obj[$propName] != "undefined") {
$propList += ($propName + "='" + obj[$propName] + "', ");
function $logProperties(obj, force = false) {
if (jmri_logging || force) {
var $propList = "";
for (var $propName in obj) {
if (typeof obj[$propName] != "undefined") {
$propList += ($propName + "='" + obj[$propName] + "', ");
}
}
jmri.log("$logProperties(obj): " + $propList + ".");
}
if (jmri_logging) jmri.log("$logProperties(obj): " + $propList + ".");
}

//process the response returned for the requestPanelXML command
Expand Down Expand Up @@ -545,6 +547,10 @@ function processPanelXML($returnedData, $success, $xhr) {
break;

case "drawn" :
if (jmri_logging) {
jmri.log("case drawm " + $widget.widgetType);
$logProperties($widget);
}
switch ($widget.widgetType) {
case "positionablepoint" :
//just store these points in persistent variable for use when drawing tracksegments and layoutturnouts
Expand Down Expand Up @@ -573,7 +579,7 @@ function processPanelXML($returnedData, $success, $xhr) {
$widget.jsonType = "turnout"; // JSON object type
$widget['x'] = $widget.xcen; //normalize x,y
$widget['y'] = $widget.ycen;
if ((typeof $widget.name !== "undefined") && ($widget.disabled !== "yes")) {
if ((typeof $widget.name !== "undefined") && ($widget.disabled !== "yes")) {
$widget.classes += $widget.jsonType + " clickable "; //make it clickable (unless no turnout assigned)
}
//set widget occupancy sensor from block to speed affected changes later
Expand All @@ -600,12 +606,6 @@ function processPanelXML($returnedData, $success, $xhr) {
jmri.getSensor($widget["occupancysensor"]); //listen for occupancy changes
break;
case "layoutSlip" :
if (jmri_logging) {
jmri.log("case layoutSlip:");
$logProperties(this);
$logProperties($widget);
}

//save the slip state to turnout state information
$widget['turnout'] = $(this).find('turnout:first').text();
$widget['turnoutB'] = $(this).find('turnoutB:first').text();
Expand Down Expand Up @@ -726,8 +726,14 @@ function processPanelXML($returnedData, $success, $xhr) {
//store this widget in persistent array, with ident as key
$widget['id'] = $widget.ident;
$gWidgets[$widget.id] = $widget;

if ($widget.bezier == "yes") {
$widget['controlpoints'] = $(this).find('controlpoint');
}

//draw the tracksegment
$drawTrackSegment($widget);

if ($widget["occupancysensor"])
jmri.getSensor($widget["occupancysensor"]); //listen for occupancy changes
break;
Expand Down Expand Up @@ -758,7 +764,8 @@ function processPanelXML($returnedData, $success, $xhr) {
var $tycen = $widget.ycen*1.0;
$raytracks.each(function(i, item) { //loop thru raytracks, calc and store end of ray point for each
var $t = [];
$t['ident'] = $widget.ident + ".5" + item.attributes['index'].value * 1; //note: .5 is due to TrackSegment.java TURNTABLE_RAY_OFFSET
//note: .5 is due to TrackSegment.java TURNTABLE_RAY_OFFSET
$t['ident'] = $widget.ident + ".5" + item.attributes['index'].value * 1;
$angle = (item.attributes['angle'].value/180.0)*Math.PI;
$t['x'] = $txcen + (($widget.radius*1.25)*Math.sin($angle)); //from getRayCoordsIndexed()
$t['y'] = $tycen - (($widget.radius*1.25)*Math.cos($angle));
Expand Down Expand Up @@ -1010,7 +1017,34 @@ function $drawTrackSegment($widget) {
if ($widget.mainline == "yes") {
$width = $gPanel.mainlinetrackwidth;
}
if (typeof $widget.angle == "undefined") {

if ($widget.bezier == "yes") {
//jmri.log("drawing bezier tracksegment " + $widget.ident + ".");

var $cps = $widget.controlpoints; // get the control points

var points = [[$pt1.x, $pt1.y]]; // first point
$cps.each(function( idx, elem ) { // control points
points.push([elem.attributes.x.value, elem.attributes.y.value]);
});
points.push([$pt2.x, $pt2.y]); // last point
$drawBezier(points, $gPanel.defaulttrackcolor, $gPanel.sidetrackwidth);

if (false) { // set true to draw construction lines thru control points
var lastX = $pt1.x, lastY = $pt1.y; //start at end point 1
$cps.each(function( idx, elem ) {
var x = elem.attributes.x.value;
var y = elem.attributes.y.value;
//draw the line from last to this control point
$drawLine(lastX, lastY, x, y, $gPanel.defaulttrackcolor, $gPanel.sidetrackwidth);
lastX = x;
lastY = y;
});
//draw the line from last to end point 2
$drawLine(lastX, lastY, $pt2.x, $pt2.y, $gPanel.defaulttrackcolor, $gPanel.sidetrackwidth);
}
} else if (typeof $widget.angle == "undefined") {
//jmri.log("drawing non-bezier tracksegment " + $widget.ident + ".");
//draw straight line between the points
if ($widget.dashed == "yes") {
$drawDashedLine($pt1.x, $pt1.y, $pt2.x, $pt2.y, $color, $width, $gDashArray);
Expand Down Expand Up @@ -1266,7 +1300,7 @@ function $drawTurnout($widget) {
}
}
}

// erase and draw turnout circles if enabled, including occupancy check
if (($gPanel.turnoutcircles == "yes") && ($widget.disabled !== "yes")) {
$drawCircle($widget.xcen, $widget.ycen, $gPanel.turnoutcirclesize * SIZE, erase, 1);
Expand All @@ -1278,7 +1312,7 @@ function $drawTurnout($widget) {
if ($widget.occupancystate == ACTIVE) {
$('#'+$widget.id).removeClass("clickable");
$('#'+$widget.id).unbind(UPEVENT, $handleClick);
} else {
} else {
$('#'+$widget.id).addClass("clickable");
$('#'+$widget.id).bind(UPEVENT, $handleClick);
}
Expand Down Expand Up @@ -1433,7 +1467,7 @@ function $drawSlip($widget) {
}

if (($gPanel.turnoutcircles == "yes") && ($widget.disabled !== "yes")) {

//draw the two control circles
var $cr = $gPanel.turnoutcirclesize * SIZE; //turnout circle radius

Expand Down Expand Up @@ -1570,10 +1604,12 @@ function $drawLine($pt1x, $pt1y, $pt2x, $pt2y, $color, $width) {
$gCtx.strokeStyle = $color;
if (typeof $width !== "undefined" && $savLineWidth != $width)
$gCtx.lineWidth = $width;

$gCtx.beginPath();
$gCtx.moveTo($pt1x, $pt1y);
$gCtx.lineTo($pt2x, $pt2y);
$gCtx.stroke();

// put color and width back to default, if changed
if ($savStrokeStyle != $color)
$gCtx.strokeStyle = $savStrokeStyle;
Expand Down Expand Up @@ -1621,6 +1657,120 @@ function $drawArc(pt1x, pt1y, pt2x, pt2y, degrees, $color, $width) {
}
}

//
//drawBezier
//
var bezier1st = true;
function $drawBezier(points, $color, $width) {
try {
var $savLineWidth = $gCtx.lineWidth;
var $savStrokeStyle = $gCtx.strokeStyle;
//only change context if needed
if (typeof $color !== "undefined" && $savStrokeStyle != $color)
$gCtx.strokeStyle = $color;
if (typeof $width !== "undefined" && $savLineWidth != $width)
$gCtx.lineWidth = $width;

bezier1st = true;
$gCtx.beginPath();
$plotBezier(points);
$gCtx.stroke();

// put color and width back to default, if changed
if ($savStrokeStyle != $color)
$gCtx.strokeStyle = $savStrokeStyle;
if ($savLineWidth != $width)
$gCtx.lineWidth = $savLineWidth;
} catch (e) {
if (jmri_logging) {
jmri.log("$plotBezier exception: " + e);
var vDebug = "";
for (var prop in e) {
vDebug += " ["+ prop+ "]: '"+ e[prop]+ "'\n";
}
vDebug += "toString(): " + " value: [" + e.toString() + "]";
jmri.log(vDebug);
}
}
}

//
//plotBezier - recursive function to draw bezier curve
//
function $plotBezier(points, depth = 0) {
var len = points.length, idx, jdx;

//jmri.log("points: " + points);

// calculate flatness to determine if we need to recurse...
var outer_distance = 0;
for (var idx = 1; idx < len; idx++) {
outer_distance += $distance(points[idx - 1], points[idx]);
}
var inner_distance = $distance(points[0], points[len - 1]);
var flatness = outer_distance / inner_distance;

// depth prevents stack overflow
// (I picked 12 because 2^12 = 2048 is larger than most monitors ;-)
// the flatness comparison value is somewhat arbitrary.
// (I just kept moving it closer to 1 until I got good results. ;-)
if ((depth > 12) || (flatness <= 1.001)) {
var p0 = points[0], pN = points[len - 1];
if (bezier1st) {
$gCtx.moveTo(p0[0], p0[1]);
bezier1st = false;
}
$gCtx.lineTo(p0[0], pN[1]);
} else {
// calculate (len - 1) order of points
// (zero'th order are the input points)
var orderPoints = [];
for (idx = 0; idx < len - 1; idx++) {
var nthOrderPoints = [];
for (jdx = 0; jdx < len - 1 - idx; jdx++) {
if (idx == 0) {
nthOrderPoints.push($midpoint(points[jdx], points[jdx + 1]));
} else {
nthOrderPoints.push($midpoint(orderPoints[idx - 1][jdx], orderPoints[idx - 1][jdx + 1]));
}
}
orderPoints.push(nthOrderPoints);
}

// collect left points
var leftPoints = [];
leftPoints.push(points[0]);
for (idx = 0; idx < len - 1; idx++) {
leftPoints.push(orderPoints[idx][0]);
}
// draw left side Bezier
$plotBezier(leftPoints, depth + 1);

// collect right points
var rightPoints = [];
for (idx = 0; idx < len - 1; idx++) {
rightPoints.push(orderPoints[len - 2 - idx][idx]);
}
rightPoints.push(points[len - 1]);

// draw right side Bezier
$plotBezier(rightPoints, depth + 1);
}
}

function $distance(p1, p2) {
var dx = p2[0] - p1[0];
var dy = p2[1] - p1[1];
return Math.sqrt((dx * dx) + (dy * dy));
}

function $midpoint(p1, p2) {
var result = [];
result.push($half(p1[0], p2[0]));
result.push($half(p1[1], p2[1]));
return result;
}

//set object attributes from xml attributes, returning object
var $getObjFromXML = function(e) {
var $widget = {};
Expand Down Expand Up @@ -1989,7 +2139,7 @@ var $getNextState = function($widget) {
for (k in $widget) {
var s = k.substr(4) * 1; //extract the state from current icon var, insure it is treated as numeric
//get valid value, name starts with 'icon', but not the HELD or DARK ones
if (k.indexOf('icon') == 0 && typeof $widget[k] !== "undefined" && k != 'icon' + HELD && k != 'icon' + DARK) {
if (k.indexOf('icon') == 0 && typeof $widget[k] !== "undefined" && k != 'icon' + HELD && k != 'icon' + DARK) {
if (typeof $firstState == "undefined")
$firstState = s; //remember the first state (for last one)
if (typeof $currentState !== "undefined" && typeof $nextState == "undefined")
Expand Down

0 comments on commit c6b1b0c

Please sign in to comment.