Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
david_stocker
Advisor
Advisor

This is part of a tutorial series on creating extension components for Design Studio.

Up to now, our gauge has been a colored arc and that's it.  Our emphasis has been on Design Studio specific concepts.  With this set of installments, we're going to start cleaning up and refining the component for professional use.  We'll be adding guide lines, a pointer, animations and text decorations.

Part 8 will be about the guide lines.  As in the early parts of this series, we're going to step out of Design Studio and investigate drawing the guide lines with raw HTML and D3, before we add them into our component.

What you see above is the goal of this installment.  We're going to draw three guide lines.  The first is a circular line at the outer radius; having its own start and end angles.  This could be used to outline the gauge, even when no arc is present (e.g. if it were empty or we were using only a pointer.  The other two start at the origin and follow the start angle and end angle-max.

Drawing the Ring Guide Line

What we're going to do here is draw a second arc, starting at outerRad and with a given thickness.  It will have its own start and end angles.  Effectively, it's a rehash of what we did waaay back, in Part 2a.  ( http://scn.sap.com/community/businessobjects-design-studio/blog/2015/10/06/part-2a--your-first-steps... )









///////////////////////////////////////////

//Lets build a border ring around the gauge


///////////////////////////////////////////


var visRing = d3.select("#content").append("svg:svg").attr("width", "100%").attr("height", "100%");


var ringThickness = 2;


var ringOuterRad = outerRad + ringThickness;  //Outer ring starts at the outer radius of the inner arc


var ringColorCode = "black";


var ringStartAngleDeg = 0;


var ringEndAngleDeg = 360;




//Don't let the arc have a negative length


if (ringEndAngleDeg < ringStartAngleDeg){













ringEndAngleDeg = ringStartAngleDeg;
alert("End angle of outer ring may not be less than start angle!");

}


var ringArcDefinition = d3.svg.arc()





















.innerRadius(outerRad)
.outerRadius(ringOuterRad)
.startAngle(ringStartAngleDeg * (pi/180)) //converting from degs to radians
.endAngle(ringEndAngleDeg * (pi/180)) //converting from degs to radians



var ringArc = vis






















.append("path")
.attr("d", ringArcDefinition)
.attr("fill", ringColorCode)
.attr("transform", "translate(" + offsetLeft + "," + offsetDown + ")");



Drawing the Start and End Guide Lines

Again, we are rehashing what we already covered in Part4b ( http://scn.sap.com/community/businessobjects-design-studio/blog/2015/12/08/your-first-extension-part... ).  There, we used a line accessor function to visualize the padding boxes by drawing them.  We'll do the same here.  What's different is that the start and end points will need to be calculated, so rather than the lineData being a simple array of x/y value pairs, it will be built dynamically.  It will use the line length (i.e. the radius of the gauge; outerRad) and the angle (either start or end) and use a bit of trigonometry to get the x and y points.

The line data is defined by the counterpoint and the points on the outer radius, where radial lines from the angle would intersect.  We'll use a helper function, endPoints(), to to the trigonometric calculation.  The looking at the function, you see that the brush stroke of the radial line on start angle actually starts at the ring and goes to the centre and then goes back out to the ring at end angle.


var lineData = [endPoints (outerRad, startAngleDeg), {x:offsetLeft, y:offsetDown}, endPoints (outerRad, endAngleDeg)];





//Helper function


function endPoints (lineLength, lineAngle){


  var endX = offsetLeft - (lineLength * Math.sin(lineAngle * (pi/180)));


  var endY = offsetDown - (lineLength * Math.cos(lineAngle * (pi/180)));


  return {x:endX, y:endY}


}




Using these two to help draw the radial lines:


///////////////////////////////////////////


//Lets build a the start and end lines


///////////////////////////////////////////


var bracketThickness = 2;


var lineData = [endPoints (outerRad, startAngleDeg), {x:offsetLeft, y:offsetDown}, endPoints (outerRad, endAngleDeg)];


var visStartBracket = d3.select("#content").append("svg:svg").attr("width", "100%").attr("height", "100%");


var lineFunction = d3.svg.line()


  .x(function(d) { return d.x; })


  .y(function(d) { return d.y; })


  .interpolate("linear");



var borderLines = vis


  .attr("width", width).attr("height", height) // Added height and width so line is visible


  .append("path")


  .attr("d", lineFunction(lineData))


  .attr("stroke", ringColorCode)


  .attr("stroke-width", bracketThickness)


  .attr("fill", "none");




//Helper function


function endPoints (lineLength, lineAngle){


  var endX = offsetLeft - (lineLength * Math.sin(lineAngle * (pi/180)));


  var endY = offsetDown - (lineLength * Math.cos(lineAngle * (pi/180)));


  return {x:endX, y:endY}



Putting it all together in a test webpage

That's it.  Next time, we'll adapt these features to the gauge component as it now stands.


<!DOCTYPE html>


<html>


  <head>


  <meta http-equiv='X-UA-Compatible' content='IE=edge' />


  <title>Part 4</title>


  <div id='content'></div>


  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>


  <script>


  var vis = d3.select("#content").append("svg:svg").attr("width", "100%").attr("height", "100%");



  var pi = Math.PI;



  //Viz definitiions


  var innerRad = 0;


  //var outerRad = 70;


  var width = 200;


  var height = 200;


  var startAngleDeg = -45;


  var endAngleDeg = 45;


  var colorCode = "red";




  //Outer Dimensions & Positioning


  var paddingTop = 10;


  var paddingBottom = 10;


  var paddingLeft = 10;


  var paddingRight = 10;



  //The total size of the component is calculated from its parts



  // Find the larger left/right padding


  var lrPadding = paddingLeft + paddingRight;


  var tbPadding = paddingTop + paddingBottom;


  var maxPadding = lrPadding;


  if (maxPadding < tbPadding){


  maxPadding = tbPadding


  }



  var outerRad = (width - 2*(maxPadding))/2;


  //var width = (outerRad * 2) + paddingLeft + paddingRight;


  //var height = (outerRad * 2) + paddingTop + paddingBottom;



  //The offset will determine where the center of the arc shall be


  var offsetLeft = outerRad + paddingLeft;


  var offsetDown = outerRad + paddingTop;



  //Don't let the arc have a negative length


  if (endAngleDeg < startAngleDeg){


  endAngleDeg = startAngleDeg;


  alert("End angle may not be less than start angle!");


  }



  var arcDef = d3.svg.arc()


  .innerRadius(innerRad)


  .outerRadius(outerRad)


  .startAngle(startAngleDeg * (pi/180)) //converting from degs to radians


  .endAngle(endAngleDeg * (pi/180)); //converting from degs to radians



  var guageArc = vis.append("path")


     .style("fill", colorCode)


     .attr("width", width).attr("height", height) // Added height and width so arc is visible


     .attr("transform", "translate(" + offsetLeft + "," + offsetDown + ")")


     .attr("d", arcDef);



  ///////////////////////////////////////////


  //Lets build a border ring around the gauge


  ///////////////////////////////////////////


  var visRing = d3.select("#content").append("svg:svg").attr("width", "100%").attr("height", "100%");


  var ringThickness = 2;


  var ringOuterRad = outerRad + ringThickness;  //Outer ring starts at the outer radius of the inner arc


  var ringColorCode = "black";


  var ringStartAngleDeg = 0;


  var ringEndAngleDeg = 360;



  //Don't let the arc have a negative length


  if (ringEndAngleDeg < ringStartAngleDeg){


  ringEndAngleDeg = ringStartAngleDeg;


  alert("End angle of outer ring may not be less than start angle!");


  }


  var ringArcDefinition = d3.svg.arc()


  .innerRadius(outerRad)


  .outerRadius(ringOuterRad)


  .startAngle(ringStartAngleDeg * (pi/180)) //converting from degs to radians


  .endAngle(ringEndAngleDeg * (pi/180)) //converting from degs to radians



  var ringArc = vis


  .append("path")


  .attr("d", ringArcDefinition)


  .attr("fill", ringColorCode)


  .attr("transform", "translate(" + offsetLeft + "," + offsetDown + ")");





  ///////////////////////////////////////////


  //Lets build a the start and end lines


  ///////////////////////////////////////////


  var bracketThickness = 2;


  var lineData = [endPoints (outerRad, startAngleDeg), {x:offsetLeft, y:offsetDown}, endPoints (outerRad, endAngleDeg)];


  var visStartBracket = d3.select("#content").append("svg:svg").attr("width", "100%").attr("height", "100%");


  var lineFunction = d3.svg.line()


  .x(function(d) { return d.x; })


  .y(function(d) { return d.y; })


  .interpolate("linear");



  var borderLines = vis


  .attr("width", width).attr("height", height) // Added height and width so line is visible


  .append("path")


  .attr("d", lineFunction(lineData))


  .attr("stroke", ringColorCode)


  .attr("stroke-width", bracketThickness)


  .attr("fill", "none");



  //Helper function


  function endPoints (lineLength, lineAngle){


  var endX = offsetLeft - (lineLength * Math.sin(lineAngle * (pi/180)));


  var endY = offsetDown - (lineLength * Math.cos(lineAngle * (pi/180)));


  return {x:endX, y:endY}


  }


  </script>


    </head>


  <body class='sapUiBody'>


  <div id='content'></div>


  </body>


</html>