LiveCode Widgets: More Line Chart Madness

by Monte Goulding on November 2, 2016 No comments

Continuing on from my last blog post where I added the showLines and markerStyles property to the line chart and not being one to know when to stop I realised that what the line chart really needs now is more marker styles. I could come up with some more styles and draw them but that’s not as much fun as what I’m going to do. What I’m going to do is allow you to specify one of the 6 marker styles already implemented and also any icon name from the svg library. We can have beautiful charts with little smiley faces as markers!

SVG Icon Line Markers

So first thing we need to do is add the SVG library as a dependency:


use com.livecode.library.iconSVG

Next for efficiency sake we are going to move as much of the path code out of the loop drawing the vertices as possible. So in the graph line loop we add the following code. The constrainPathToRect handler is from the widget utils module:


variable tFilledMarkers as Boolean
variable tMarkerPath as Path
		
if mVerticesShow is true then
   if mMarkerStyles[tGraphLine - 1] contains "circle" then
      put circle path centered at point [0,0] with radius 3 into tMarkerPath
			
      put mMarkerStyles[tGraphLine - 1] contains "filled" into tFilledMarkers
   else if mMarkerStyles[tGraphLine - 1] contains "square" or mMarkerStyles[tGraphLine - 1] contains "diamond" then
      put rectangle path of rectangle [-3,-3,3,3] into tMarkerPath
				
      if mMarkerStyles[tGraphLine - 1] contains "diamond" then
         rotate tMarkerPath by 45
      end if
				
      put mMarkerStyles[tGraphLine - 1] contains "filled" into tFilledMarkers
   else
      put path iconSVGPathFromName(mMarkerStyles[tGraphLine - 1]) into tMarkerPath
      constrainPathToRect(rectangle [-3,-3,3,3],tMarkerPath)
      put true into tFilledMarkers
   end if
end if

Then in the vertices drawing loop we just need to add our marker at the correct location to the vertices. Here we are translating the path from the 0,0 origin to the graph point then translating it back so it is ready for the next vertices. It would be feasible to translate it relative to its previous translation but this code is much simpler.


// Draw vertices
if mVerticesShow is true then
   translate tMarkerPath by [tPointX, tPointY]
   add tMarkerPath to tPathVertices
   translate tMarkerPath by [-tPointX, -tPointY]
end if

That is almost it. We just need a slight change to the drawing command to use the tFilledMarkers variable:


if mVerticesShow is true and mMarkerStyles[tGraphLine - 1] is not "" then
   if tFilledMarkers then
      fill tPathVertices on this canvas
   else
      stroke tPathVertices on this canvas
   end if
end if

And now we test…

svgplot

Well.. your eyesight is probably better than mine but I’m thinking we need a way to scale the markers so they can be a more reasonable size!

The markerScale

We need to implement a new property markerScale which is a scale factor to apply to markers scaling from the default 8×8 point marker. First we write a property definition and documentation:


/**
Summary: A scale factor to apply to markers

Syntax:
set the markerScale of  to 
get the markerScale of 

Value (real): A scale factor to apply to markers

Description:
The default scale factor is 1 which corresponds to an 8x8 point marker.

Related: markerStyles (property), showLines (property)

*/
property markerScale get mMarkerScale set SetMarkerScale
metadata markerScale.default is "1"
metadata markerScale.editor is "com.livecode.pi.number"
metadata markerScale.step is "0.05"
metadata markerScale.min is "0"
metadata markerScale.max is "5"
metadata markerScale.label is "Marker scale"

We need to add the mMarkerScale instance variable


private variable mMarkerScale as Real

In the OnCreate handler we need to set the default value:


put 1 into mMarkerScale

In the OnSave handler we need to store the value:


put mMarkerScale into rProperties["markerScale"]

In the OnLoad handler we need to read the value:


if "markerScale" is among the keys of pProperties then
   put pProperties["markerScale"] into mMarkerScale
end if

We need a setter so that we can redraw the chart when the value changes:


public handler SetMarkerScale(in pMarkerScale as Real) returns nothing
   put pMarkerScale into mMarkerScale
   redraw all
end handler

Now all we need to do is scale our markers when painting. First we set the value of a size variable to use in sizing the markers:


variable tMarkerSize as Real
put mMarkerScale * 4 into tMarkerSize

Then we update our marker path creation code to scale the markers:


if mMarkerStyles[tGraphLine - 1] contains "circle" then
   put circle path centered at point [0,0] with radius tMarkerSize into tMarkerPath
			
   put mMarkerStyles[tGraphLine - 1] contains "filled" into tFilledMarkers
else if mMarkerStyles[tGraphLine - 1] contains "square" or mMarkerStyles[tGraphLine - 1] contains "diamond" then
   put rectangle path of rectangle [-tMarkerSize,-tMarkerSize,tMarkerSize,tMarkerSize] into tMarkerPath
				
   if mMarkerStyles[tGraphLine - 1] contains "diamond" then
      rotate tMarkerPath by 45
   end if
				
   put mMarkerStyles[tGraphLine - 1] contains "filled" into tFilledMarkers
else
   put path iconSVGPathFromName(mMarkerStyles[tGraphLine - 1]) into tMarkerPath
   put true into tFilledMarkers
   constrainPathToRect(rectangle [-tMarkerSize,-tMarkerSize,tMarkerSize,tMarkerSize],tMarkerPath)
end if

The End Result

Now when we create a chart and set the markerSize to 1.5 we get some lovely big markers.

You can review the code changes on GitHub here.

svgplot2

Monte GouldingLiveCode Widgets: More Line Chart Madness

Related Posts

Take a look at these posts

Join the conversation

*