Perspective, faster page

Okay, I had a bit of free time today, so here is a complete SVG solution. You could do the Label as SVG text as well, or a gauge needle if you wanted, I haven't gone that far. Just a little example of the power of using the Ignition Designer to add SVG elements. I didn't include the label for the example.

Note: All of this (outside of the calculations needed to find the points for the shape) was done inside of the designer (yes I can be somewhat of a masochist at times. :rofl:).

First a simple example:

image

I keep a blank svg file handy, for simple shapes and such. I started this by embedding that file into the view.

File XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
  xmlns:svg="http://www.w3.org/2000/svg"
  xmlns="http://www.w3.org/2000/svg"
  width="50mm"
  height="50mm"
  viewBox="0 0 50 50"
  version="1.1">
</svg>

I then modified the viewBox to something a bit more reasonable for this application, in this case 0 0 300 300

I then added elements to the elements array as needed.

For the simple example, you will need 2 elements for the Clip-Paths, 2 elements for the rectangles that will be clipped, and 1 element for the border.

I created two clipPath elements, one for the meter, and one for the background. The background looks like this:
image

The clipPath elements need to have an id, this is important because it's how the rectangle elements reference the path they need to be clipped by.

Full Path
M0.7963 165.436 A150 150 0 1 1 299.2037 165.436 
A10 10 0 1 1 279.2567 164.4069 A130 130 0 1 0 20.7433 164.4039 
A10 10 0 1 1 0.7963 165.436

Use your favorite SVG reference for help decoding that to understand what really going on.

The clipPath for the meter is where the "magic" happens, so to speak. Everything is pretty much the same for it as for the last path, the exception is there is a binding on the path (d value).
image

For the binding I added a custom prop to the SVG, that I called value. I am treating it like a percentage, but it could be whatever you want it to be. The binding is a property binding on the value custom prop with a script transform. The script transform looks at the value and makes decisions about the best way to draw the gauge. At, 0 and 100% it short circuits to already known paths. Otherwise it calculates the new path.

Script Transform
def transform(self, value, quality, timestamp):
	import math
	### full path: M0.7963 165.436 A150 150 0 1 1 299.2037 165.436 A10 10 0 1 1 279.2567 164.4069 A130 130 0 1 0 20.7433 164.4039 A10 10 0 1 1 0.7963 165.436
	
	if value == 100:
		return "M0.7963 165.436 A150 150 0 1 1 299.2037 165.436 A10 10 0 1 1 279.2567 164.4069 A130 130 0 1 0 20.7433 164.4039 A10 10 0 1 1 0.7963 165.436"
	
	if value == 0:
		return "M0.7963 165.436 A10 10 0 1 1 20.7433 164.4039 A10 10 0 1 1 0.7963 165.436"
	
	calPath = "M0.7963 165.436 A150 150 0 "
	calPath += "1 1 " if value >= 94 else "0 1"
	
	outsideStopX = 150 - 150 * math.cos(math.radians((200 * value / 100) - 15))
	outsideStopY = 150 -150 * math.sin(math.radians((200 * value/100) - 15 ))
	insideStopX = 150 - 130 * math.cos(math.radians((200 * value/100)-15))
	insideStopY = 150 - 130 * math.sin(math.radians((200 * value/100) - 15))
	
	calPath += str(outsideStopX) + " " + str(outsideStopY) + " A10 10 0 1 1 " + str(insideStopX) + " " + str(insideStopY) + " A130 130 0 "
	calPath += "1 0 " if value >= 95 else "0 0"
	calPath += "20.7433 164.4039 A10 10 0 1 1 0.7963 165.436"
		
	return calPath

It is important that the clipPath elements be defined prior to any elements that will reference them.

Finally I have two rectangle elements, one which is clipped by the meterPath and one that is clipped by the bgPath. They look like this:
image

Notice the order, the order of elements determines the "z-index" so to speak. Basically, the elements are added to the DOM in the order they appear in the array, and later elements in the DOM are rendered on top of those that come before.

Also, notice that I have set the clipPath for the rectangles to the clipPaths that we defined earlier, using the url() function with the selector for that id.

Finally, I have added a border element because I think it looks better. This is just a path with the fill set to none, and the stroke defined. The path is the same one used for the background clip path.
image

Simple gauge example images

0%
image

25%
image

50%
image

75%
image

100%
image

If you want to get fancy, then you can fill the meter rectangle with a gradient. To add a gradient, add a defs element to top to the elements array, and give it an id, x1,y1,x2, y2 the these two points define the line along which the gradient is drawn, in this case it is just a horizontal line across the full width. Also add an elements array, add an element for each stop that you want defined in your gradient. I have used 4 stops.

Linear gradient definition

Then, set the fill paint for the meter rectangle to the gradient, using the url function, the result is a meter with gradient fill.
image

Gradient Fill Gauge Examples

0%
image
50%
image
100%
image

11 Likes