Customize a mark

In this guide you'll see how to:

✔️ Style your signs (data points) depending on their values

✔️ Go further by creating a custom mark decorator

✔️ Use your decorator in your charts

✔️ Complete or replace the default decorator

Find a similar Javascript example code here: https://play.data2viz.io/sketches/wYwzBL/edit/

Case study

You have a chart and want to highlight values that have a specific "status". You also want to use a custom "selector" in the form of a small red triangle, and add a custom label when a value is critical.

Customize%20a%20mark%2042c68e968d624715956a49562c9eacb9/Untitled.png

Let's see what you need to do to create this.

Mark styles

Most standard marks have default styles that can be overriden.

Simple property

When you look at Charts-kt reference, you can see that some styles are straightforward, for example, the curve value of a LineMark is a simple property that defines how to draw the line: straight, curved...

Customize%20a%20mark%2042c68e968d624715956a49562c9eacb9/Untitled%201.png

See reference: https://charts-kt.io/doc/core/io.data2viz.charting.chart.mark.internal/-has-curve/index.html

If you set the curve property to Curved, your whole line will be drawn with curved lines:

Customize%20a%20mark%2042c68e968d624715956a49562c9eacb9/Untitled%202.png

"Dimension property"

Some other styles are not constant, they are computed when drawing, based on a given Dimension, like the style strokeColor for the LineMark which is the property used to get the color of the line.

Customize%20a%20mark%2042c68e968d624715956a49562c9eacb9/Untitled%203.png

See reference: https://charts-kt.io/doc/core/io.data2viz.charting.chart.mark.internal/-has-stroke-color/index.html

With this type of property, you can define some values that return a ColorOrGradient? value for a given DOMAIN object.

Let's see an example of what you can do. You have domain objects you want to plot on screen, and these objects have a status property:

chart(data) { val id = discrete({ domain.id }) val value = quantitative({ domain.value }) plot(id, value) { strokeColor = discrete( { when (domain.status) { Status.OK -> Colors.Web.mediumseagreen Status.Normal -> Colors.Web.black Status.Warning -> Colors.Web.orangered Status.Error -> Colors.Web.red } } ) } }

Your strokeColor is now a discrete Dimension, this dimension takes the status property of your domain object, and returns a Color for each of its values.

When the PlotMark draw function is called, it will compute the strokeColor for each point:

Customize%20a%20mark%2042c68e968d624715956a49562c9eacb9/Untitled%204.png

Custom decorators

If you want to go further and replace the default style of your mark, you have to create a custom decorator.

It is very easy in Chart-kt using aDatumDecorator. A DatumDecorator is a lambda that takes 3 parameters:

  • datum: Datum the datum created from your business object
  • position: Point the expected position of your sign (may depend on the Mark type)
  • drawingZone: DrawingZone the screen zone where you're expected to draw, all coordinates are relative to this zone

You can of course refer to other properties from your chart, but these 3 parameters already allow most customizations.

Let's have a look at a very simple decorator:

myMarkDecorator = { datum, position, drawingZone -> drawingZone.rect { x = position.x - 3.0 y = position.y - 3.0 width = 6.0 height = 6.0 stroke = strokeColor(datum) fill = Colors.Web.orange } }

Customize%20a%20mark%2042c68e968d624715956a49562c9eacb9/Untitled%205.png

Of course, this example is for your understanding only, because if you had wished to do that, simply changing the symbol and the strokeColor properties of your PlotMark would have done the trick.

Custom decorators are interesting as they can rely on several properties. For example, you may wish to display icons, but only if enough space is available. In a case like this, you can bind the decoration to the Chart.zoom.xZoom.ratio value or your Scene.width for example.

Plug-in custom decorators

When you have created custom decorators, you want to use them in your charts.

Standard Charts-kt marks have implements several interfaces when they can use custom decorators:

  • HasMarkDecorator this mark can use a custom decorator for every sign
  • HasHighlightDecorator this mark can use a custom decorator for highlighted sign
  • HasSelectionDecorator this mark can use a custom decorator for a selected sign

If your Mark implements one or all of these interfaces, you can plug your custom decorator on it. Let's look at the standard one, HasMarkDecorator which contains:

  • val defaultMarkDecorator: DatumDecorator<DOMAIN>
  • var markDecorator: DatumDecorator<DOMAIN>

The defaultMarkDecorator is, as its name implies, the decorator used by default.

You may want to use it when creating a custom decorator. In this case, don't forget to call it in your lambda.

The markDecorator property is the way to "plug" your custom decorator.

Example implementation

Now let's get back to your chart, you want to go from this:

Customize%20a%20mark%2042c68e968d624715956a49562c9eacb9/Untitled%206.png

To this:

Customize%20a%20mark%2042c68e968d624715956a49562c9eacb9/Untitled%207.png

So you need to create:

  • a "status" decorator for the marks that draws the colored circles over them
  • a "selection" decorator that display a little red triangle and a message based on the status property of our data point
chart(data) { val id = discrete({ domain.id }) val value = quantitative({ domain.value }) // my values line(id, value) { strokeColor = constant(Colors.Web.black) curve = Markcurves.Straight // the "mark status" decorator markDecorator = { datum, position, drawingZone -> // first, call the default decorator defaultMarkDecorator(datum, position, drawingZone) // then add the custom decoration if (datum.domain.status == Status.Normal) return drawingZone.circle { x = position.x y = position.y radius = 5.0 strokeWidth = 2.0 stroke = when (datum.domain.status) { Status.OK -> Colors.Web.mediumseagreen Status.Warning -> Colors.Web.orangered Status.Error -> Colors.Web.red } } } // the "highlight" decorator displaying the triangle selector and the label // we won't detail this one here, you can find the code on the samples page highlightDecorator = customHiglightDecorator() } }