# GoJS Brushes

A Brush holds color information and describes how to draw the inside of a Shape or the stroke of a shape or a TextBlock or the background of any GraphObject.

A Brush must not be modified once it has been assigned to a GraphObject, such as the Shape.fill or TextBlock.stroke or GraphObject.background. However, a Brush may be shared by multiple GraphObjects.

## Solid Brushes

The simplest brushes are defined by a single solid color. Because they are so simple, anywhere you want a single-color brush you can substitute a valid CSS color string.

      diagram.add(
        new go.Part()
          .add(
            new go.Shape("Circle", {
                fill: new go.Brush({ color: "palegreen" })
              })
          ));

      diagram.add(
        new go.Part()
          .add(
            new go.Shape("Circle", {
                fill: "palegreen"
              })
          ));

Many CSS color strings are valid, including named colors, hex values, RGB values, and RGBA values.

      diagram.layout = new go.GridLayout();

      diagram.add(
        new go.Part()
          .add(
            new go.Shape("Circle", {
              fill: "#DFAD83"
            })
          ));

      diagram.add(
        new go.Part()
          .add(
            new go.Shape("Circle", {
              fill: "rgba(0,255,0,.3)" // semi transparent green
            })
          ));

      diagram.add(
        new go.Part()
          .add(
            new go.Shape("Circle", {
                fill: "rgba(0,255,0,.3)",
                stroke: '#DFBB00',
                strokeWidth: 4,
                background: 'coral'
              })
          ));

## Gradient Brushes

Gradient brushes are defined by setting the type and adding a number of color stops to the Brush.

      // constructs a Linear gradient brush
      const brush = new go.Brush(go.BrushType.Linear);
      brush.addColorStop(0, "blue");
      brush.addColorStop(1, "red");

To simplify the syntax, you can use the constructor's initialization argument:

      // constructs the same Brush
      const brush = new go.Brush("Linear", { 0.0: "blue", 1.0: "red" });

Some examples follow:

      diagram.add(
        new go.Part("Table")
          .add(
            new go.Shape({ row: 0, column: 0,
                figure: "Circle", width: 100, height: 100, margin: 5,
                // A linear gradient brush from blue to red, going from top to bottom (default)
                fill: new go.Brush("Linear", { 0.0: "blue", 1.0: "red" })
              }),
            new go.Shape({ row: 0, column: 1,
                figure: "Circle", width: 100, height: 100, margin: 5,
                // A linear gradient brush from blue to red, going from bottom to top
                // by defining start and end spots
                fill: new go.Brush("Linear", { 0.0: "blue", 1.0: "red", start: go.Spot.Bottom, end: go.Spot.Top })
              })
        ));

Brushes can have any number of color stops:

      diagram.add(
        new go.Part("Table")
          .add(
            new go.Shape({ row: 0, column: 0,
                figure: "Rectangle", width: 100, height: 100, margin: 5,
                // A rainbow linear gradient brush:
                fill: new go.Brush("Linear", {
                  0.0: "rgba(255, 0, 0, 1)",
                  0.15: "rgba(255, 255, 0, 1)",
                  0.30: "rgba(0, 255, 0, 1)",
                  0.50: "rgba(0, 255, 255, 1)",
                  0.65: "rgba(0, 0, 255, 1)",
                  0.80: "rgba(255, 0, 255, 1)",
                  1: "rgba(255, 0, 0, 1)"
                })
              }),
            new go.Shape({ row: 0, column: 1,
                figure: "Rectangle", width: 100, height: 100, margin: 5,
                // A rainbow radial gradient brush:
                fill: new go.Brush("Radial", {
                  0.0: "rgba(255, 0, 0, 1)",
                  0.15: "rgba(255, 255, 0, 1)",
                  0.30: "rgba(0, 255, 0, 1)",
                  0.50: "rgba(0, 255, 255, 1)",
                  0.65: "rgba(0, 0, 255, 1)",
                  0.80: "rgba(255, 0, 255, 1)",
                  1: "rgba(255, 0, 0, 1)"
                })
              })
          ));

Radial gradient brushes can be controlled with Brush.startRadius and Brush.endRadius, which default to zero and NaN, respectively, meaning the gradient begins at the very center and goes to the farthest measured edge of the object.

      diagram.layout = new go.GridLayout();

      diagram.add(
        new go.Part()
          .add(
            new go.Shape({
                figure: "Rectangle", width: 100, height: 100, margin: 5,
                // A rainbow radial gradient brush:
                fill: new go.Brush("Radial", {
                  0.0: "red", 1: "black"
                })
              })
          ));

      diagram.add(
        new go.Part()
          .add(
            new go.Shape({
                figure: "Rectangle", width: 100, height: 100, margin: 5,
                // A rainbow radial gradient brush:
                fill: new go.Brush("Radial", {
                  startRadius: 30, 0.0: "red", 1: "black"
                })
              })
          ));

      diagram.add(
        new go.Part()
          .add(
            new go.Shape({
                figure: "Rectangle", width: 100, height: 100, margin: 5,
                // A rainbow radial gradient brush:
                fill: new go.Brush("Radial", {
                  startRadius: 30, endRadius: 40, 0.0: "red", 1: "black"
                })
              })
          ));

Several GraphObjects can share the same Brush:

      diagram.layout = new go.GridLayout();

      // Create one brush for several GraphObjects to share:
      const rainbow = new go.Brush("Linear", {
                            0.0: "rgba(255, 0, 0, 1)",
                            0.15: "rgba(255, 255, 0, 1)",
                            0.30: "rgba(0, 255, 0, 1)",
                            0.50: "rgba(0, 255, 255, 1)",
                            0.65: "rgba(0, 0, 255, 1)",
                            0.80: "rgba(255, 0, 255, 1)",
                            1: "rgba(255, 0, 0, 1)"
                          });
      diagram.add(
        new go.Part()
          .add(
            new go.Shape({ figure: "Rectangle", width: 100, height: 100, fill: rainbow })
          ));

      diagram.add(
        new go.Part()
          .add(
            new go.Shape({ figure: "Fragile", width: 50, height: 50, angle: 45, fill: rainbow })
          ));

      diagram.add(
        new go.Part("Auto")
          .add(
            new go.Shape({ figure: "Rectangle", fill: rainbow }),
            new go.TextBlock("text", { font: 'bold 32pt sans-serif', stroke: rainbow, angle: 90 })
          ));

      diagram.add(
        new go.Part()
          .add(
            new go.Shape({ figure: "Circle", width: 70, height: 70, angle: 180,
                          fill: null, strokeWidth: 10, stroke: rainbow })
          ));

## Pattern Brushes

The following example sets up two Pattern brushes, one using an HTML Canvas with content drawn to it, which looks like this:

The other Pattern Brush uses this image:

      // set up an 40x40 HTML Canvas and draw on it to create a repeating "tile" to use as a pattern
      function makePattern() {
        const patternCanvas = document.createElement('canvas');
        patternCanvas.width = 40;
        patternCanvas.height = 40;
        const pctx = patternCanvas.getContext('2d');

        // This creates a shape similar to a diamond leaf
        pctx.beginPath();
        pctx.moveTo(0.0, 40.0);
        pctx.lineTo(26.9, 36.0);
        pctx.bezierCurveTo(31.7, 36.0, 36.0, 32.1, 36.0, 27.3);
        pctx.lineTo(40.0, 0.0);
        pctx.lineTo(11.8, 3.0);
        pctx.bezierCurveTo(7.0, 3.0, 3.0, 6.9, 3.0, 11.7);
        pctx.lineTo(0.0, 40.0);
        pctx.closePath();
        pctx.fillStyle = "rgb(188, 222, 178)";
        pctx.fill();
        pctx.lineWidth = 0.8;
        pctx.strokeStyle = "rgb(0, 156, 86)";
        pctx.lineJoin = "miter";
        pctx.miterLimit = 4.0;
        pctx.stroke();

        return patternCanvas;
      }

      if (window.goSamples) goSamples();  // init for these samples -- you don't need to call this

      diagram.nodeTemplate =
        new go.Node("Spot", { resizable: true, resizeObjectName: 'SHAPE' })
          .add(
            new go.Shape("Rectangle", { name: 'SHAPE', strokeWidth: 0, stroke: null })
              .bind("fill"),
            new go.TextBlock({ margin: 10, font: "bold 18px Verdana" })
              .bind("text")
          );

      const img = new Image();
      img.src = 'images/pattern.jpg';

      // Use an image as a pattern
      const patternBrush = new go.Brush("Pattern", { pattern: img });
      // use a reference to an HTML Canvas (with renderings on it) as a pattern:
      const patternBrush2 = new go.Brush("Pattern", { pattern: makePattern() });

      diagram.model = new go.GraphLinksModel(
        [
          { key: 1, text: "Alpha", fill: patternBrush },
          { key: 2, text: "Beta",  fill: patternBrush2 }
        ]);

The result:

## Brush Functions

There are some functions available for generating different colors or modifying Brush colors:

- Brush.randomColor - returns a random hexadecimal color value
- Brush.lightenBy and Brush.darkenBy - return lightened or darkened colors/brushes; there are both instance methods and static functions
- Brush.lighten and Brush.darken - convenience static functions which return lightened or darkened colors
- Brush.mix - mixes two colors together
- Brush.isDark - determines whether a color is dark, often used in bindings; there are both instance methods and static functions

In the following example, parts use the lighten and darken functions to get suitable colors for their stroke/fill.

      diagram.layout = new go.GridLayout();
      const color1 = "rgb(80, 130, 210)";
      const color2 = go.Brush.randomColor(192, 224);
      const gradBrush = new go.Brush("Linear", { 0: color1, 1: color2 });
      function shapeStyle() {
        return { figure: "Ellipse", width: 120, height: 80, strokeWidth: 4 };
      }

      // static Brush methods
      diagram.add(
        new go.Part("Auto")
          .add(
            new go.Shape(shapeStyle(), {
              fill: color1, stroke: go.Brush.darken(color1)
            }),
            new go.TextBlock("dark stroke")
          ));

      diagram.add(
        new go.Part("Auto")
          .add(
            new go.Shape(shapeStyle(), {
              fill: color1, stroke: go.Brush.darkenBy(color1, .4)
            }),
            new go.TextBlock("darker stroke")
          ));

      diagram.add(
        new go.Part("Auto")
          .add(
            new go.Shape(shapeStyle(), {
              fill: go.Brush.lighten(color1), stroke: color1
            }),
            new go.TextBlock("light fill")
          ));

      diagram.add(
        new go.Part("Auto")
          .add(
            new go.Shape(shapeStyle(), {
              fill: go.Brush.lightenBy(color1, .4), stroke: color1
            }),
            new go.TextBlock("lighter fill")
          ));

      // instance Brush methods
      diagram.add(
        new go.Part("Auto")
          .add(
            new go.Shape(shapeStyle(), {
              fill: gradBrush.copy().lightenBy(.2)
            }),
            new go.TextBlock("lighter")
          ));

      diagram.add(
        new go.Part("Auto")
          .add(
            new go.Shape(shapeStyle(), {
              fill: gradBrush
            }),
            new go.TextBlock("normal")
          ));

      diagram.add(
        new go.Part("Auto")
          .add(
            new go.Shape(shapeStyle(), {
              fill: gradBrush.copy().darkenBy(.2)
            }),
            new go.TextBlock("darker")
          ));

In the following example, the color of text is determined by whether the background shape is dark.

      diagram.layout = new go.GridLayout();

      diagram.nodeTemplate =
        new go.Node("Auto", { desiredSize: new go.Size(80, 40) })
          .add(
            new go.Shape("RoundedRectangle", { strokeWidth: 0 })
              .bind("fill", "color"),
            new go.TextBlock({ margin: 8 })
              .bind("stroke", "color",
                    // dark nodes use white text, light nodes use dark text
                    c => go.Brush.isDark(c) ? "white" : "black")
              .bind("text")
          );

      diagram.model = new go.Model(
        [
          { text: "Alpha", color: "white" },
          { text: "Beta", color: "black" },
          { text: "Gamma", color: "darkblue" },
          { text: "Delta", color: "lightblue" },
          { text: "Epsilon", color: "darkgreen" },
          { text: "Zeta", color: "lightgreen" },
          { text: "Eta", color: "darkred" },
          { text: "Theta", color: "lightcoral" }
        ]
      )