My options to fill a shape

I need to create a shape like this:

If I define it as 2 half-circles and a rectangle, like the following:

[{ id: 1, _svgDef: “F M20,40 A25,25 0 1,1 70,40 Z”},
{ id: 2, _svgDef: “F M20,40 L70,40 L 70,90 L 20,90 Z”},
{ id: 3, _svgDef: “F M20,90 A25,25 0 0,0 70,90 Z”}]

The best I was able to get using Linear and Radial brushes look like this:

The only way I found so far to get correct fill of the shape is to define 2 full circles, apply Radial brush starting in the center and overlap them with a rectangle. The issue with this is: I cannot make such an object semi-transparent.
Do I have to use an external image to fill parts of my shape?
Or can I extend Brush?

Do I have to use an external image to fill parts of my shape?

You may have to, yes.

Easiest would be using the image itself in a Picture GraphObject.

Somewhat more difficult, but doable, would be making a Shape with a specific Geometry that is the same dimensions as the image. Then using a GoJS Brush of type Pattern to paint the image onto the Shape:

fill: $(go.Brush, go.Brush.Pattern, { pattern: someHTMLImageElement })

Otherwise, this kind of this is probably difficult to do in GoJS. (I can’t think of an easy way right now, at least)<span =“Apple-tab-span” style=“white-space:pre”>

I think this works:

[code] var $ = go.GraphObject.make;
var white = “rgba(255,255,255,0.5)”;
var color = “rgba(127,127,255,0.5)”;
var endbrush = $(go.Brush, go.Brush.Radial, { 0: white, 1: color });
var midbrush = $(go.Brush, go.Brush.Linear, { 0: color, 0.5: white, 1: color });

myDiagram.nodeTemplateMap.add("SpecialBrush",
  $(go.Part, "Table",
    { resizable: true, rotatable: true },
    new go.Binding("desiredSize", "size").makeTwoWay(),
    $(go.RowColumnDefinition, { column: 0, sizing: go.RowColumnDefinition.None }),
    $(go.Panel,
      { column: 0, stretch: go.GraphObject.Vertical },
      new go.Binding("width", "size", function(s) { return s.height/2; }),
      $(go.Shape, "Circle",
        { stretch: go.GraphObject.Fill },
        new go.Binding("width", "size", function(s) { return s.height; }),
        { stroke: null, strokeWidth: 0, fill: endbrush })
    ),
    $(go.RowColumnDefinition, { column: 1, sizing: go.RowColumnDefinition.ProportionalExtra }),
    $(go.Shape,
      { column: 1, stretch: go.GraphObject.Fill },
      { stroke: null, strokeWidth: 0, fill: midbrush }),
    $(go.RowColumnDefinition, { column: 2, sizing: go.RowColumnDefinition.None }),
    $(go.Panel,
      { column: 2, stretch: go.GraphObject.Vertical, angle: 180 },
      new go.Binding("width", "size", function(s) { return s.height/2; }),
      $(go.Shape, "Circle",
        { stretch: go.GraphObject.Fill },
        new go.Binding("width", "size", function(s) { return s.height; }),
        { stroke: null, strokeWidth: 0, fill: endbrush })
    )
  ));

myDiagram.model.addNodeData({ category: "SpecialBrush", size: new go.Size(140, 40) });[/code]

Here is the above Part plus a copy that was resized:

This uses clipping of the two Circle Shapes to just show half of the circular gradient.

This also uses data binding to maintain the correct widths as the user resizes the whole Part. Of course if you don’t need to support resizing, you can hard-code the actual width and height values.

But when the total width is less than the height, it just clips the right end of the whole part. One could customize the ResizingTool to prevent that.

Thank you, it works great!Emoticons
The only question I have is: there are very thin lines (maybe 1 px) between central and rounded parts. Can I get rid of them?

That’s probably due to anti-aliasing artifacts in what I assume is a subpixel gap between the two filled areas. I didn’t notice that when I created the example code, above.

Does it depend on the actual size and/or position of the Shapes?

Seems like you are right, depends on an angle and size of the image, such gaps appear and disappear.

Here’s a modification that will fix the hairline spacing issue.

NOTE that this relies of the brush being fully opaque though, as the left side and right side are actually entire circles in this template. This means there cannot possibly be a tiny spacing issue, but you sacrifice toggling opacity.

var $ = go.GraphObject.make;
var white = "rgba(255,255,255,1)";
var color = "rgba(127,127,255,1)";
var endbrush = $(go.Brush, go.Brush.Radial, { 0: white, 1: color });
var midbrush = $(go.Brush, go.Brush.Linear, { 0: color, 0.5: white, 1: color });

myDiagram.nodeTemplateMap.add("SpecialBrush",
  $(go.Part, "Table",
    { resizable: true, rotatable: true },
    new go.Binding("desiredSize", "size").makeTwoWay(),
    // left segment
    $(go.RowColumnDefinition, { column: 0, sizing: go.RowColumnDefinition.None },
      new go.Binding("width", "size", function(s) { return s.height/2; })),
    $(go.Panel,
      { column: 0, columnSpan: 2, stretch: go.GraphObject.Vertical, alignment: go.Spot.Left },
      new go.Binding("width", "size", function(s) { return s.height; }),
      $(go.Shape, "Circle",
        { stretch: go.GraphObject.Fill },
        new go.Binding("width", "size", function(s) { return s.height; }),
        { stroke: null, strokeWidth: 0, fill: endbrush })
    ),

    // right segment
    $(go.RowColumnDefinition, { column: 2, sizing: go.RowColumnDefinition.None },
      new go.Binding("width", "size", function(s) { return s.height/2; })),
    $(go.Panel,
      { column: 1, columnSpan: 2, stretch: go.GraphObject.Vertical, angle: 180, alignment: go.Spot.Right },
      new go.Binding("width", "size", function(s) { return s.height; }),
      $(go.Shape, "Circle",
        { stretch: go.GraphObject.Fill },
        new go.Binding("width", "size", function(s) { return s.height; }),
        { stroke: null, strokeWidth: 0, fill: endbrush })
    ),

    // middle segment
    $(go.RowColumnDefinition, { column: 1, sizing: go.RowColumnDefinition.ProportionalExtra }),
    $(go.Shape,
      { column: 1, stretch: go.GraphObject.Fill },
      { stroke: null, strokeWidth: 0, fill: midbrush })
  ));

myDiagram.model.addNodeData({ category: "SpecialBrush", size: new go.Size(140, 40) });