Background of complex geometry node

Hello,

I have a complex geometry of a node and I want to assign it a background witch is an image.

The image will have to fill all the complex geometry.

The image is square but the the node is not.

What can I do?

Thanks!

Did you want to have the image fill the Shape, and be clipped by the Shape’s geometry?

One answer is to use a “Pattern” Brush as the Shape.fill. Basically if you have an HTML Image element, you can set or bind the Shape.fill to $(go.Brush, “Pattern”, { pattern: someHTMLImageElement }). However if Shapes might be different sizes, you would not be able to share the same “Pattern” Brush, since the fill pattern is repeated as the shape’s area gets bigger or clipped as the shape’s area becomes smaller.

Another answer is to define the Shape.geometry so that it acts as a mask, with a hollow area showing the Picture underneath.

Yet another answer is to use an unsupported, undocumented feature of “Spot” and “Auto” Panels to clip the non-main elements to the geometry of the main element. I don’t know if you’d like to try that or not.

For an example of the first possibility:

    myDiagram.nodeTemplate =
      $(go.Node,
        $(go.Shape,
          {
            fill: "lightgreen", strokeWidth: 2,
            geometryString: "F1 M30 0 L60 60 0 60z"
          },
          new go.Binding("geometryString", "geo"),
          new go.Binding("fill", "src", function(src) {
            var img = document.createElement("img");
            img.src = src;
            img.style.width = 60;
            img.style.height = 60;
            document.body.appendChild(img);
            return $(go.Brush, "Pattern", { pattern: img });
          })
        )
      );

    myDiagram.model = new go.GraphLinksModel([
      { key: "Alpha", src: "http://gojs.net/latest/samples/images/55x55.png" },
      { key: "Beta", src: "http://gojs.net/latest/samples/images/55x55.png",
        geo: "F1 M20 0 L60 0 60 40 40 60 0 60 0 20z" }
    ],[
      { from: "Alpha", to: "Beta" }
    ]);

This produces:

Hi Walter,

I did what you suggested, but the image is not uniform fill but too big. I tries changing the size but it didnt work :

This is the original picture:

Any suggestions?

Ah, that’s because the image is bigger than the size of the shape, and there’s no transform on the image in the pattern brush.

OK, here’s option 3:

    myDiagram.nodeTemplate =
      $(go.Node, "Auto",
        { isClipping: true },  // cause the Shape's geometry to clip the other element(s)
        { width: 100, height: 100 },  // considerably bigger than the actual image, to show stretched image
        $(go.Shape,
          { portId: "" },  // links connect with edge of shape, not with rectangular Node
          { geometryString: "F1 M30 0 L60 60 0 60z" },  // default to a triangle
          new go.Binding("geometryString", "geo")),
        $(go.Picture,
          { stretch: go.GraphObject.Fill },  // needed to stretch picture to fill whole area of shape
          new go.Binding("source", "src"))
      );

    // same model as with previous option
    myDiagram.model = new go.GraphLinksModel([
      { key: "Alpha", src: "http://gojs.net/latest/samples/images/55x55.png" },
      { key: "Beta", src: "http://gojs.net/latest/samples/images/55x55.png",
        geo: "F1 M20 0 L60 0 60 40 40 60 0 60 0 20z" }
    ],[
      { from: "Alpha", to: "Beta" }
    ]);

Note the use of the undocumented and not yet supported property Panel.isClipping. This causes the geometry of the Shape to be used to clip the rest of the panel. That shape is not rendered, so its fill and stroke properties are ignored, although its strokeWidth property does affect the size of the shape, as it normally does.

Note also that because the whole “Auto” Panel has a desired size, the Shape is given that size. Because Shape.geometryStretch stretches by default, its Geometry is scaled to fit.

And because it’s an “Auto” Panel and because the Picture is stretched to fill and because the Picture.imageStretch also stretches by default, the image fills the whole area. But of course it is clipped because the Panel is clipping to the Shape’s geometry.

This results in the following diagram:

I have moved a node to make it clear that links go to the edge of whatever geometry the shape has, not to the edge of the rectangular area of the whole node.

Walter, I want the shape to have more properties such as stroke and more.

I tried defining these properties but with no luck - no effect.

Why is that?

When isClipping is true, only the Shape’s Geometry is used as a mask – it does not do any drawing of its own.

But you’re welcome to use another Shape with the same Geometry in the same position that draws in the normal fashion.

return g(go.Node, 'Auto', { width: 100, height: 100, isClipping: true }, g(go.Shape,{ geometryString: "F1 M30 0 L60 60 0 60z" }), g(go.Shape,{ geometryString: "F1 M30 0 L60 60 0 60z", stroke: '#cdcdcd', strokeWidth: 2 }), g(go.Picture,{ stretch: go.GraphObject.Fill }, // needed to stretch picture to fill whole area of shape new go.Binding("source", "mockupUrl") )

This the code I tried, what am I doing wrong?

You need to use another Panel, since the presence of Panel.isClipping will limit what you can show in that one Panel.

Continuing my sample, maybe something like this:

    myDiagram.nodeTemplate =
      $(go.Node, "Auto",
        $(go.Shape,  // this is the normal border in an Auto Panel
          { portId: "" },  // links connect with edge of shape, not with rectangular Node
          { fill: null, stroke: "cyan", strokeWidth: 4 },
          { geometryString: "F1 M30 0 L60 60 0 60z" },  // default to a triangle
          new go.Binding("geometryString", "geo")),
        $(go.Panel, "Auto",
          { isClipping: true },  // cause the Shape's geometry to clip other element(s)
          { width: 100, height: 100 },  // bigger than the actual image, to demo stretch
          $(go.Shape,
            { geometryString: "F1 M30 0 L60 60 0 60z" },  // default to a triangle
            new go.Binding("geometryString", "geo")),
          $(go.Picture,
            { stretch: go.GraphObject.Fill },  // stretch picture to fill whole area of shape
            new go.Binding("source", "src"))
        )
      );

Note that I moved the port declaration to be the outer Shape – not necessary, but I think it’s likely what you want.

Thank you Walter, I got it and it is displaying nicely!

Now for advanced requirements :)

I need to fill the node like UniformToFill, here is the example :

Now in your example, I cant achieve it because I define size and because of that I loose ratio. Picture number 1 is displaying disordered and picture 2 is good.

this is the result of the code:

In summary:

  1. Always keep the aspect ratio
  2. Never crop the width
  3. The left corner of the image will be placed on the left corner of the node and the same for the right corner.
  4. If the image covers more then the node height -> crop the rest of the image.
  5. If the image height is smaller then the node height -> align the image in the center.
    As well as the width.

Thanks!

Did you try setting Picture | GoJS API ?

Yes, it didnt work for me.

This is my node :

return g(go.Node, 'Auto',
				{
					alignmentFocus: go.Spot.TopLeft,
					avoidableMargin: new go.Margin(-25, 0, 0, 0)
				},
				g(go.Shape, {
						name: 'MainShape',
						geometry: actionGeometry,
						width: 118,
						height: 78,
						fill: '#f0f0f0',
						fromLinkable: true,
						toLinkable: true,
						margin: new go.Margin(15, 5, 0, 0),
						stroke: '#cdcdcd',
						strokeWidth: 2
					},
					new go.Binding('stroke', 'isSelected', (b) => b ? '#16c4d9' : '#cdcdcd').ofObject(),
					new go.Binding('strokeWidth', 'isSelected', (b) => b ? 4 : 2).ofObject(),
					new go.Binding('margin', 'isSelected', (b) => b ? new go.Margin(14, 5, 0, -1) : new go.Margin(15, 5, 0, 0)).ofObject()
				),
				g(go.Panel, "Auto",
					{
						isClipping: true,
						width: 116,
						height: 78
					},
					g(go.Shape,
						{
							geometry: actionGeometry,
							spot1: go.Spot.TopLeft,
							spot2: go.Spot.BottomRight
						}
					),
					g(go.Picture,
						{
							maxSize: new go.Size(116, NaN),
							imageStretch: go.GraphObject.Uniform,
							cursor: diagramMode.isPreview ? 'pointer' : '',
							click: (e, obj) => {
								if (diagramMode.isPreview) {
									if (obj.part.data.showMockup) {
										$window.open(obj.part.data.fullSizeMockUrl, "_blank", "width=800, height=600")
									}
								}
							}
						},
						new go.Binding('source', 'mockupUrl')
					),
					new go.Binding('visible', 'showMockup')
				)
			);

Do you have any ideas?

Thanks a lot!