Dimensions des éléments d'un modèle

Bonjour,

En javascript, quel attribut permet de connaître la largeur et la hauteur de l’élement d’un node dans un modèle de données ? En débuggant je vois que height et width font 30, or mes formes sont rectangle donc je ne dois pas regarder au bon endroit.

Version=4.0.P24
BuiltOn=2020-09-08 14:02 (revision cc3e1c0a38d7b0549d655541696b9efd34201031)

Merci !
Emmanuelle

Vous pouvez accéder à l’élément SVG qui a ses propres propriétés (width, height si elles sont spécifiées dans le DOM). Et au meta-données que le modeleur lui assigne, au debugger vous verrez entre autre les propriétés “w” et “h” qui donne la dimension calculée.

var nodeElement = $('.node[data-obj="myObject"][data-id="123"]');
var nodeData = nodeElement.data("d");

Il y a des accesseurs au niveau desktop getNode, getNodeContent, getLink, getContainer.
Et si vous voulez en changer, il faut passer par les méthodes du node, du link…

Alors je regarde bien au bon endroit mais je ne comprends pas pourquoi j’ai du 30x30 sur un rectangle ?

image

C’est la taille quand elle est forcée par code ou redimensionnée (si le node est redimensionnable), là je pense qu’elle reste à la taille min 30x30 vu que l’affichage est par défaut (classe empty) pas de resize.

Pour avoir la taille réelle affichée, il faut regarder l’élément SVG qui spécifie la taille du rectangle en bordure :

var border = $('.node[data-obj="myObject"][data-id="123"]  .border-item');
var w = border.width() // à tester car pas sur que jQuery sache mesurer un élément SVG
var w = parseInt(border.attr("width")); // sinon directement l'attribut

1 Like

C’est encore moi :-)

J’ai l’impression que les vraies dimensions ne sont disponibles qu’une fois le modèle affiché.
Dans le OnDrawNode j’ai encore les dimensions par défaut (30x30).
Or je cherche un moyen d’aligner mes formes correctement en fonction de leur largeur, à la création du modèle.

Y a-t-il un moyen ?
Merci d’avance !

Il me semble normal que les dimensions soient accessibles une fois le composant affiché et pas avant.
c’est à dire tout son contenu parcouru une fois pour connaitre la taille de chaque libellé, bordure…

Le hook front onDrawNode sert à surcharger les propriétés avant affichage, afficher par défaut ou en spécifique, puis faire ce que vous voulez une fois affiché.

Au plus simple commencez par débugger la taille après le display :

onDrawNode: function(n, display) {
	display();
	console.log("nw = " + n.w);
	console.log("nh = " + n.h);
	var s = n.size(); // recalcul de la taille suivant le border s'il existe
	console.log("sw = " + s.w);
	console.log("sh = " + s.h);
	// changer de taille
	n.size(200,200);
}

Exemple dans le Démo où un Produit est remplacé par son image, et la commande est affichée par défaut :

// Synchronous rendering
onDrawNode: function(n, display) {
	var self = this, pad=10,
		elt = Simplicite.Diagram.createElement;

	var imgField = self.template.imgFields[n.object];
	if (imgField && n.data) {
		// Border
		var b = n.border = elt("rect", { x:0, y:0, width:0, height:0 });
		b.addClass("border").appendTo(n.elt);
		n.radius && b.attr({ rx: n.radius, ry: n.radius });
		if (n.shadow) b.attr("filter","url(#shadow)");
		else b.removeAttr("filter");
		n.bind(b);
	
		// Picture
		var x = pad,
			imgId = n.data[imgField],
			imgName = "pict_"+n.object+"_"+n.id;
		if (imgId) {
			self.desktop.getDefImage(imgName, 50, 0)
				.appendTo(n.elt)
				.attr({ x:x, y:pad });
			x += 50+pad;
		}
		
		// Product infos
		elt("text", { x:x, y:pad, dy:"1em"}).text(n.label).appendTo(n.elt).css("font-weight", "bold");
		if (n.object=="DemoSupplier") {
			elt("text", { x:x, y:pad, dy:"2em"}).text(n.data.demoSupName).appendTo(n.elt);
		} else if (n.object=="DemoProduct") {
			elt("text", { x:x, y:pad, dy:"2em"}).text(n.data.demoPrdName).appendTo(n.elt);
			elt("text", { x:x, y:pad, dy:"3em"}).text("Unit price: "+n.data.demoPrdUnitPrice).appendTo(n.elt);
		}

		// Set final size of border
		var box = n.elt[0].getBBox();
		n.size(box.width + pad*2, box.height + pad*2);
	} else { // Default drawing for other objects
		display();
	}
}

Le display est sensé être synchrone pour que la taille soit connue tout de suite.
Si ça se trouve c’est ça qui fait que la taille n’est pas tout de suite disponible, par exemple si le SVG doit charger une image en asynchrone, faire un GET ajax…

Après le display j’ai toujours du 30x30.
Sinon y a-t-il moyen d’organiser les node dans un hook après le display ?
Là j’ai plus de 200 formes, à organiser à la main c’est l’enfer !

Il y a le hook ready quand le desktop a terminé l’ouverture d’un modèle (avec toujours le problème des chargements asynchrones s’il y en a).

Mais si vous insérez des nodes simples un par un / ou via la méthode create, c’est à vous de le faire à la fin de tous vos chargements dans le callback de cette méthode.

J’ai testé et je retrouve bien la dimension de chaque noeud après le display quand je les insère un à un. Si votre noeud est redimensionnable ou pas, il faut regarder w/x ou wmin/hmin qui est la taille minimale.
Normalement ça vaut la même chose si le noeud n’est pas redimensionnable ou avec une shape spécifique.

Sinon donnez moi tout votre code que je reproduise le problème.

Vous pouvez aussi regarder la taille du border SVG accessible dans le node qui est un rectangle englobant, il y a la position et la taille dans ses propriétés.

C’est peut-être juste un paramètre que je définis mal ?

Voici mon code (ne pas faire attention aux bidouillages avec les positions, j’essaie de contourner en calculant approximativement la largeur …)

function openBaseMap(obj)
{
	//Search model
	var app = $ui.getAjax();
	var modSearch = app.getBusinessObject("Model");
	var contSearch = app.getBusinessObject("RciMacroprocess");
	var nodesList = [];
	

	
	modSearch.search(function(listSearch) {
				
				//If diagram exists opening it
				if (listSearch && listSearch.length) {
					var url = app._baseURL + '/ui/modeler?object=Model&action=svg&row_id=' + listSearch[0].row_id;
					window.open(url,'_blank');
				}
				else
				//Create it
				{
					fetchContainers();
				}
				
	},{
			  mod_name: "MacroProcess base map"
			}
	);
	
	modSearch.resetFilters();
	
	function fetchContainers()
	{
		console.log("enter fetch cont");
		//contSearch.resetFilters();
		//contSearch.getField("rciMpId").setFilter("is null");
		contSearch.search(function(listCont) {
					console.log("enter fetch cont cbk");
				//Insert level 1 MP as containers
				for (var i = 0 ; i < listCont.length ; i++)
				{
						nodesList.push({
							object: "RciMacroprocess", // node object
							id: listCont[i].row_id, // node row_id
							template: "MacroProcess", // node template name
							x: i*30 + 30, // dummy position
							y: i*70 + 30,
							contId: 0,
							container: true 
						});
				}
				
				fetchMacroProcess();
				
				console.log("exit fetch cont cbk");
				},
				{
					/*rciMpId: "is null",*/
					rciMpLevel: 1
				}
			);
			console.log("exit fetch cont");
		//contSearch.resetFilters();
	}
	
	function fetchMacroProcess()
	{
			console.log("enter fetch mp");
		var mpSearch = app.getBusinessObject("RciMacroprocess");
		//contSearch.resetFilters();
		//contSearch.getField("rciMpId").setFilter("is null");
		mpSearch.resetFilters();
		mpSearch.search(function(listMp) {
				console.log("enter fetch mp cbk");
				//Insert level 2, 3 ... MP as content
				
				var posX = 35;
				var posY = 35;
				
				for (var i = 0 ; i < listMp.length ; i++)
				{
						if (posX + (listMp[i].rciMpLabelen.length * 10) > 1000)
						{
							posX = 35;
							posY += 50;
						}
							
						nodesList.push({
							object: "RciMacroprocess", // node object
							id: listMp[i].row_id, // node row_id
							template: "SubMpprocess", // node template name,
							contId:listMp[i].rciMpId,
							x: posX,
							y: posY
						});
						
						posX += (listMp[i].rciMpLabelen.length * 10) + 5;
				}
				
				sortNodes();
				createDiagram();
				console.log("exit fetch mp cbk");
				},
				{
					rciMpLevel: "in ('2','3')",
					rciMpId: 1
				}
			);
				console.log("exit fetch mp");
	}
	
	function sortNodes()
	{
		nodesList.sort(compareContainers);
	}
	
	function compareContainers(a, b) {
	  // Use toUpperCase() to ignore character casing
	  const contA = a.rciMpId;
	  const contB = b.rciMpId;
	
	  let comparison = 0;
	  if (contA > contB) {
	    comparison = 1;
	  } else if (contA < contB) {
	    comparison = -1;
	  }
	  return comparison;
	}
	
	function createDiagram()
	{console.log("enter create");
		$ui.loadDiagramEngine(function() {
			try {
				$ui.diagram.create("BasemapMacroProcess", "MacroProcess base map", {
						hidden: true, // hide the modeler
						nodes: nodesList  // nodes to insert
					},
					function(diagram) {
						// close the hidden model
						diagram.close(false);
						// Re-open in a window
						$ui.displayModeler(null, diagram.modelId, {
							svg: true,
							docked: false
						});
					});
				}
			catch(e) {
				console.log(e);
			}
		});
		console.log("exit create");
	}
}

Voici ce que j’ai après le display, du 30x30

Et après affichage, en revanche, c’est correct

Oui 30x30 est la taille minimale en dur si la dimension ne peut pas être connue.

Ca veut dire que le “SVG caché” ne peut pas bien calculer les tailles. Simplicité utilise la méthode getBBox qui, je suppose, doit retourner 0x0 si l’objet n’est pas affiché ou dessiné.

Essayez de créer le modèle avec hidden: false
pour voir si les tailles sont alors accessibles (mettez des console.log dans votre template ou un debugger, car c’est pas facile de charger cette VM dynamique)

Sinon votre positionnement devra se faire après affichage :
$ui.displayModeler(null, diagram.modelId, { svg: true, docked: false });
en codant le hook ready du template.

Enfin, positionner 200 nodes sans chevauchement et pour que la lecture du graph orienté soit user-friendly est un problème NP complet, comment prévoyez vous de le faire ? Les entreprises embauchent généralement des humains/architectes pour faire ce genre de modèle. Attention de ne pas partir dans des choses trop complexes, ou trop longues en exécution.