Cannot remove padding for Clusters with HTML Labels

Hello, I am trying to create a cluster with an HTML (table) label that includes an image and some text. However, despite setting padding and borders to 0 and justification to left the label image has some padding around it which results in a gap. How can I fix this? I have marked these gaps with red arrows here. I want them to be exactly aligned to the corner of of the box instead.

gaps

The .dot code is

subgraph cluster_AWSgroup_42484 {
				graph [center=true fontname="Sans-Serif" fontsize=12 label=<<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0"><TR><TD><img src="./resource_images/aws/general/aws.png"/></TD><TD>AWS Cloud</TD></TR></TABLE>> labeljust=l margin=100 ordering=in pencolor=black rankdir=LR shape=box style=solid]
			}

If this is something that needs to be amended in the C code, please point me to the relevant sections so I can raise a PR. Willing to pay to get this fixed.

Thanks.

No C hacks, but here is a post-processing hack that seems to do what you want. The margin/pad around the image seems to have multiple components:

  • an invisible table/cell border
  • a pad that keeps the label from colliding with the periphery box around the cluster.

This hack uses

(Linux) shell script:

f=clusterPad2.gv
T=png
F=`basename $f .gv`;dot -Tdot $f  |
gvpr -c -f shiftLabel.gvpr |
neato -n2 -T$T >$F.$T

Here is shiftLabel.gvpr:

BEGIN{
  double bbMinX, bbMinY, bbMaxX, bbMaxY, lpX, lpY, lpMinX, lpMaxY, newX, newY;

  // recursive routine, processing from the top (root) down
  graph_t labelShift (graph_t Gr) {
    graph_t thisG;

    for (thisG = fstsubg(Gr); thisG; thisG=nxtsubg(thisG)) {
          thisG = labelShift(thisG);
    }
    //print("// (sub)graph: ", Gr.name);
    if (match(Gr.name,"cluster")==0 ){
      if (hasAttr(Gr, "_shift")){
        sscanf (Gr.bb, "%lf,%lf,%lf,%lf", &bbMinX, &bbMinY, &bbMaxX, &bbMaxY);
	lpX=xOf(Gr.lp);
	lpY=yOf(Gr.lp);
	lpMinX=lpX-((Gr.lwidth*72.)/2.);
	lpMaxY=lpY+((Gr.lheight*72.)/2.);
	//print("// bb: ", Gr.bb, "  lpMinX: ", lpMinX, "  lpMaxY: ", lpMaxY);
	Gr._oldlp=Gr.lp;
	// 2 fudge factor  (maybe nonexistent table border?)
	newX=lpX-(lpMinX-bbMinX)-2;
	newY=lpY+(bbMaxY-lpMaxY)+2;
	Gr.lp=sprintf("%.1f,%.1f", newX, newY);
      }
    }
    return Gr;
  } // end of labelShift
}
BEG_G{
  labelShift($G);
}

Result:

Whoops, forgot to note that you need to add a _shift attribute to the cluster (e.g.
graph [_shift=1])

Is this dot? We might need a minimum separation of one pixel to avoid degeneracy. It might take more work to hunt down why this is a problem and fix it. The spline router is funny thing and doesn’t like zero-width rectangles.

Thank you so much Steve this was an amazing idea and worked like a charm! You’re a genius.

However, I am afraid I am not familiar with gvpr / neato so I need some more guidance.

Could you help me understand and fix the following outer cluster border which seems to be jagged after post processing. When I zoom in there is a ghost outline of sorts as shown in this image

border

Many thanks and please let me know if it would be easier to talk directly on E-mail/DM. Let me know how if I can tip you or something :slight_smile:

@scnorth Yes this is the dot engine producing .dot files from the python graphviz library.

@scnorth @steveroush Thanks for your kind assistance

What does

degeneracy

mean in this context?

Does the word “Shared” show up anywhere in your graph? That is part of the overlayed text.

Degenerate spline constraint polygons- for example where nonconsecutive segments touch.

Points in general position” is another bugaboo of geometric algorithms, for example, polygons where two consecutive points are at the same position or segments are collinear can be problematic. It was just a lot of blood, sweat and tears for us to either make algorithms or robust or eliminate that from happening. You can’t just jitter the points because that can create other problems, e.g.self-intersecting polygons are no good.

Yes fixed thst thanks, any thoughts about the border?

The fudge factors need to be adjusted. Do you want the image just inside the periphery (the box surrounding the cluster) or on top of the periphery?

@steveroush I’m not sure what a fudge factor is

I’d like the image just inside if possible, although any other similar outcome overlapping on top to give the same effect is fine too since the border will be same color as the background of the image in most cases.

Fudge factor (Fudge factor - Wikipedia).
The above GVPR program shifted an “extra” 2 pixels in both X & Y directions (the fudge factors). I think I know why they are needed, but not worth the detective work to figure out exactly why they are needed.
The modified version (below) only shifts Y by 1 pixel. It also allows you to set X & Y fudge factors like this _shift="3,2.2" (it triggers if the value contains a comma)
So try this new version of shiftLabel.gvpr:

BEGIN{
  double bbMinX, bbMinY, bbMaxX, bbMaxY, lpX, lpY, lpMinX, lpMaxY, newX, newY;
  double fudgeX, fudgeY;
  string fld[int];
  
  // recursive routine, processing from the top (root) down
  graph_t labelShift (graph_t Gr) {
    graph_t thisG;

    for (thisG = fstsubg(Gr); thisG; thisG=nxtsubg(thisG)) {
          thisG = labelShift(thisG);
    }
    if (match(Gr.name,"cluster")==0 ){
      if (hasAttr(Gr, "_shift") && Gr._shift!=""){
        fudgeX=2.;  // aim for just inside periphery  - increase to shift left
        fudgeY=1.;  // aim for just inside periphery  - increase to shift up
	if (index(Gr._shift,",")>0){
	  split(Gr._shift,fld,",");
	  print("// flds : ", fld[0],"  ",fld[1]);
	  fudgeX=(double)fld[0];
	  fudgeY=(double)fld[1];
	}
        sscanf (Gr.bb, "%lf,%lf,%lf,%lf", &bbMinX, &bbMinY, &bbMaxX, &bbMaxY);
	lpX=xOf(Gr.lp);
	lpY=yOf(Gr.lp);
	lpMinX=lpX-((Gr.lwidth*72.)/2.);
	lpMaxY=lpY+((Gr.lheight*72.)/2.);
	print("// bb: ", Gr.bb, "  lpMinX: ", lpMinX, "  lpMaxY: ", lpMaxY);
	Gr._oldlp=Gr.lp;
	// fudge factors set below  (maybe nonexistent table border?)
        print("// fudge : ", fudgeX, "  ",fudgeY);
	newX=lpX-(lpMinX-bbMinX)-fudgeX;
	newY=lpY+(bbMaxY-lpMaxY)+fudgeY;
	Gr.lp=sprintf("%.1f,%.1f", newX, newY);
      }
    }
    return Gr;
  } // end of labelShift
}
BEG_G{
  labelShift($G);
}

1 Like

Thanks Steve - works like magic!

Please ignore my previous question.