1. Introduction

Mxgraph internally uses a tree data structure to record the content of the graph document. In the content tree, each node represents a graph element, and the element information is stored on the node object. The parent-child relationship of nodes means that the graph of the parent node contains the graph of the child node, which can be used to realize the functions of stratification and grouping of graphs. For example, for the following graph:

Content trees are the underlying data model for graphical documents, a bit like VDOM for React; Vnode to VUE. Many of mxGraph’s functions are built around the content tree. For example, the renderer mxCellRenderer renders graphical documents from the content tree. Codec mxCellCodec realizes the conversion between external storage format and content tree. The basic unit of operation for various layout algorithms is also the content tree node. Therefore, an in-depth understanding of the content tree model can lay a solid foundation for the subsequent source code learning process!

The content tree model is implemented by the following classes:

  1. MxGraphModel: Content tree model, mainly to implement a series of tree structure operation methods.
  2. MxCell: a content tree node used to store status information of elements such as graphs, connections, and layers.
  3. MxGeometry: The geometric information of tree nodes, which records the width, height and coordinate positions of graphic elements; The geometric characteristics of the start node and end node of the line.

This paper focuses on the usage of content tree node mxCell, and the contents of mxGraphModel and mxGeometry will be introduced in special chapters later.

2. Meet mxCell

MxCell classes defined in javascript/SRC/js/model/mxCell js file and the constructor signatures: function mxCell (value, geometry, style), parameter interpretation:

  • value: Used to define the content of the primitive. Dom objects and strings can be passed in.
  • geometry: the geometric value of the graph. For vertex type, the x, y, width and height attributes of the graph are recorded. Edge also records the points where the two ends of the line segment are connected.
  • style: Graphic style

The following example shows how to construct an mxCell object:

// Construct an mxCell instance
const cell = new mxCell('Hello world! '.new mxGeometry(60.80.100.100), 'fillColor=#ddd');
// Set cell to geometric pattern type
cell.vertex = true;

// Use the cell object to construct the model
const model = new mxGraphModel(cell);
// Render the model
new mxGraph(document.getElementById('container'), model);
Copy the code

Render result:

Tip:

Mxgraph’s style functionality is an extension of the CSS style architecture. In addition to the standard style attributes, mxGraph defines many of its own style attributes, which will be covered in a separate section.

3. Use mxCell to draw pictures

3.1 the rectangular

When vertex=true, mxCell defaults to a rectangle, so you can render a rectangle by specifying the starting coordinates, width and height attributes, for example:

// Construct an mxCell instance
const cell = new mxCell(
	null.// Specify the width, height, and starting coordinates of the rectangle by mxGeometry
	new mxGeometry(60.80.100.100));// Set cell to geometric pattern type
cell.vertex = true;
Copy the code

Render effect:

3.2 line

The logic for drawing line segments using mxCell is a little more complicated, snippet:

const cell = new mxCell('Hello world! '.new mxGeometry(), 'strokeWidth=3; ');
// Set cell to a line segment
cell.edge = true;
// Set the starting point
cell.geometry.setTerminalPoint(new mxPoint(60.180), true);
// Set the endpoint
cell.geometry.setTerminalPoint(new mxPoint(230.20), false);
Copy the code

The position of a line segment is determined by the position of the endpoints at both ends of the line segment. For example, after initializing mxCell in the above example, setTerminalPoint is also needed to set the starting and ending position of the line segment to render normally. Render result of the previous example:

Further, you can use the points attribute to split multiple segments within a line segment, for example:

const cell = new mxCell('Hello world! '.new mxGeometry(), 'strokeWidth=3; ');
// Set cell to a line segment
cell.edge = true;
// Set the starting point
cell.geometry.setTerminalPoint(new mxPoint(60.180), true);
// Set the endpoint
cell.geometry.setTerminalPoint(new mxPoint(230.20), false);
// Define multiple intermediate nodes using points
cell.geometry.points = [new mxPoint(70.50), new mxPoint(120.80)];
Copy the code

Render effect:

3.3 More built-in graphics

In addition to rectangles and line segments, mxGraph has built-in support for other graphs. Let’s start with an example:


      
<html lang="zh-CN">
	<head>
		<meta charset="utf-8" />
		<meta http-equiv="X-UA-Compatible" content="IE=edge" />
		<meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
		<title>mxgraph Example</title>
		<style>
			#container {
				background: url('.. /.. /assets/grid.gif');
			}
		</style>
		<script type="text/javascript">
			mxBasePath = '/ / cdn.jsdelivr.net/npm/[email protected]/javascript/src';
		</script>
	</head>
	<body>
		<div id="container" style="width: 400px; height: 400px;"></div>

		<script src="/ / cdn.jsdelivr.net/npm/[email protected]/javascript/mxClient.min.js"></script>
		<script type="text/javascript">
			function run(container) {
				const shapes = [
					// mxGraph has built-in support for the following graphs:
					'actor'.'cloud'.'cylinder'.'doubleEllipse'.'ellipse'.'hexagon'.'image; image=https://jgraph.github.io/mxgraph/docs/images/mxgraph_logo.gif'.'rectangle'.'rhombus'.'swimlane'.'triangle',];const root = new mxCell(null.new mxGeometry(), null);
				for (let i = 0; i < shapes.length; i++) {
					const shape = shapes[i];
					const xOffset = i % 4,
						yOffset = Math.floor(i / 4),
						x = xOffset * 100 + 20,
						y = yOffset * 100 + 20;
					const geometry = new mxGeometry(x, y, 60.60);
					const cell = new mxCell(
						shape.split('; ') [0],
						geometry,
						// Shape specifies the shape category of the cell
						`shape=${shape}; verticalLabelPosition=bottom; spacingBottom=40`
					);
					cell.vertex = true;
					root.insert(cell);
				}
				const model = new mxGraphModel(root);
				new mxGraph(container, model);
			}

			window.addEventListener('load', () => {
				run(document.getElementById('container'));
			});
		</script>
	</body>
</html>
Copy the code

Example effects:

In this example, when constructing the mxCell instance, we need to set the cell’s graphic category by using the style parameter. Core code:

const cell = new mxCell(
	null.new mxGeometry(0.0.100.100),
	// Modify the shape category using the shape attribute
	`shape=triangle`
);
Copy the code

If it is an image type, we also need to pass the image address through image:

const cell = new mxCell(
	null.new mxGeometry(0.0.100.100),
	// Shape specifies the bit image
	// the image property sets the image address
	`shape=image; image=https://jgraph.github.io/mxgraph/docs/images/mxgraph_logo.gif`
);
Copy the code

3.4 Custom graphics classes

In mxGraph, the base class of all graphics is mxShape. Users only need to inherit this class to define new graphics categories.

// 1. Inherit the mxShape class
class CustomShape extends mxShape {
	constructor() {
		super(a); }// The rendering method of the graph
	paintBackground(c, x, y, w, h) {}
}

// 2. Register the graphics class in the mxCellRenderer
mxCellRenderer.registerShape('customShape', CustomShape);

const cell = new mxCell(
	null.new mxGeometry(100.50.50.100),
	// 3. When creating the mxCell, use the shape attribute of the style parameter to define the shape category
	'shape=customShape'
);
Copy the code

Complete example:


      
<html lang="zh-CN">
	<head>
		<meta charset="utf-8" />
		<meta http-equiv="X-UA-Compatible" content="IE=edge" />
		<meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
		<title>mxgraph Example</title>
		<style>
			#container {
				background: url('.. /.. /assets/grid.gif');
			}
		</style>
		<! -- Sets the basepath for the library if not in same directory -->
		<script type="text/javascript">
			mxBasePath = '/ / cdn.jsdelivr.net/npm/[email protected]/javascript/src';
		</script>
	</head>
	<body>
		<div id="container" style="width: 300px; height: 200px;"></div>

		<script src="/ / cdn.jsdelivr.net/npm/[email protected]/javascript/mxClient.min.js"></script>
		<script type="text/javascript">
			// Inherit mxShape base class, extend custom graphics class
			class CustomShape extends mxShape {
				constructor() {
					super(a); } paintBackground(c, x, y, w, h) { c.translate(x, y);// Head
					c.ellipse(w / 4.0, w / 2, h / 4);
					c.fillAndStroke();

					c.begin();
					c.moveTo(w / 2, h / 4);
					c.lineTo(w / 2, (2 * h) / 3);

					// Arms
					c.moveTo(w / 2, h / 3);
					c.lineTo(0, h / 3);
					c.moveTo(w / 2, h / 3);
					c.lineTo(w, h / 3);

					// Legs
					c.moveTo(w / 2, (2 * h) / 3);
					c.lineTo(0, h);
					c.moveTo(w / 2, (2 * h) / 3); c.lineTo(w, h); c.end(); c.stroke(); }}// Graphics need to be registered in the renderer mxCellRenderer
			mxCellRenderer.registerShape('customShape', CustomShape);

			function run(container) {
				const cell = new mxCell(
					'Hello world! '.new mxGeometry(100.50.50.100),
					// The shape property of the style argument still specifies the shape category
					'shape=customShape'
				);
				cell.vertex = true;

				const model = new mxGraphModel(cell);
				new mxGraph(container, model);
			}

			window.addEventListener('load', () => {
				run(document.getElementById('container'));
			});
		</script>
	</body>
</html>
Copy the code

Example effects:

3.5 Drawing with Stencils

In addition to implementing custom graphics classes by extending mxShape, you can also define new graphics classes using the Stencils interface. The main steps are:

// 1.xml format defines graphic content
const shapes = `
      
       ... 
      `;
// 2. Convert strings into DOM objects
const node = new DOMParser().parseFromString(shapes, 'text/xml');
// 3. Register the stencils object
mxStencilRegistry.addStencil('or'.new mxStencil(node.firstChild));

const cell = new mxCell(
	null.new mxGeometry(100.50.50.100),
	// 4. Create the mxCell using the shape attribute of the style parameter
	'shape=or'
);
Copy the code

Complete example:


      
<html lang="zh-CN">
	<head>
		<meta charset="utf-8" />
		<meta http-equiv="X-UA-Compatible" content="IE=edge" />
		<meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
		<title>mxgraph Example</title>
		<style>
			#container {
				background: url('.. /.. /assets/grid.gif');
			}
		</style>
		<! -- Sets the basepath for the library if not in same directory -->
		<script type="text/javascript">
			mxBasePath = '/ / cdn.jsdelivr.net/npm/[email protected]/javascript/src';
		</script>
	</head>
	<body>
		<div id="container" style="width: 300px; height: 200px;"></div>

		<script src="/ / cdn.jsdelivr.net/npm/[email protected]/javascript/mxClient.min.js"></script>
		<script type="text/javascript">
			// Define graphic content in XML form
			const shapes = `<shapes>
          <shape name="or" aspect="variable">
              <background>
                  <path>
                      <move x="0" y="0" />
                      <quad x1="100" y1="0" x2="100" y2="50" />
                      <quad x1="100" y1="100" x2="0" y2="100" />
                      <close/>
                  </path>
              </background>
              <foreground>
                  <fillstroke/>
              </foreground>
          </shape>
      </shapes>`;
			// Parse the XML value of the string into a DOM object
			const parser = new DOMParser();
			const node = parser.parseFromString(shapes, 'text/xml');
			// Register brushes
			mxStencilRegistry.addStencil('or'.new mxStencil(node.firstChild));

			function run(container) {
				const cell = new mxCell(null.new mxGeometry(100.50.50.100), 'shape=or');
				cell.vertex = true;

				const model = new mxGraphModel(cell);
				new mxGraph(container, model);
			}

			window.addEventListener('load', () => {
				run(document.getElementById('container'));
			});
		</script>
	</body>
</html>
Copy the code

Example effects:

Tip:

Both custom mxShape and Stencils interface can be extended into new graphics categories. MxShape is defined in the form of classes, which has certain development costs, but can be customized with more graphics logic. Stencils can be defined by external XML files, which makes development and management more convenient. The specific mode can be selected based on actual requirements.

Both sets of methodologies are quite complex, and the author will open a special topic later to give a detailed introduction.

4. To summarize

That’s pretty much the end of this section. This paper mainly introduces the underlying data structure mxCell and how to use mxCell to draw different graphs. As we learn more, we find more points to explore, including:

  1. How to use mxGraphModel
  2. How does the style system in mxGraph work
  3. How to perform geometric transformation of figures
  4. Custom graphics class, custom stencils use method
  5. .

In the future, this series of articles will be added gradually to help those who need to understand the usage and principles of mxGraph. Those who are interested are welcome to follow.