UNPKG

create-gojs-kit

Version:

A CLI for downloading GoJS samples, extensions, and docs

804 lines (747 loc) 35.8 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"/> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover"/> <meta name="description" content="A simple process flow or SCADA diagram editor, simulating equipment monitoring and control." /> <meta itemprop="description" content="A simple process flow or SCADA diagram editor, simulating equipment monitoring and control." /> <meta property="og:description" content="A simple process flow or SCADA diagram editor, simulating equipment monitoring and control." /> <meta name="twitter:description" content="A simple process flow or SCADA diagram editor, simulating equipment monitoring and control." /> <link rel="preconnect" href="https://rsms.me/"> <link rel="stylesheet" href="../assets/css/style.css"> <!-- Copyright 1998-2025 by Northwoods Software Corporation. --> <meta itemprop="name" content="Process Flow SCADA Diagram with Tanks, Valves, Pipes and Animated Flow" /> <meta property="og:title" content="Process Flow SCADA Diagram with Tanks, Valves, Pipes and Animated Flow" /> <meta name="twitter:title" content="Process Flow SCADA Diagram with Tanks, Valves, Pipes and Animated Flow" /> <meta property="og:image" content="https://gojs.net/latest/assets/images/screenshots/processflow.png" /> <meta itemprop="image" content="https://gojs.net/latest/assets/images/screenshots/processflow.png" /> <meta name="twitter:image" content="https://gojs.net/latest/assets/images/screenshots/processflow.png" /> <meta property="og:url" content="https://gojs.net/latest/samples/processFlow.html" /> <meta property="twitter:url" content="https://gojs.net/latest/samples/processFlow.html" /> <meta name="twitter:card" content="summary_large_image" /> <meta property="og:type" content="website" /> <meta property="twitter:domain" content="gojs.net" /> <title> Process Flow SCADA Diagram with Tanks, Valves, Pipes and Animated Flow | GoJS Diagramming Library </title> </head> <body> <!-- This top nav is not part of the sample code --> <nav id="navTop" class=" w-full h-[var(--topnav-h)] z-30 bg-white border-b border-b-gray-200"> <div class="max-w-screen-xl mx-auto flex flex-wrap items-start justify-between px-4"> <a class="text-white bg-nwoods-primary font-bold !leading-[calc(var(--topnav-h)_-_1px)] my-0 px-2 text-4xl lg:text-5xl logo" href="../"> GoJS </a> <div class="relative"> <button id="topnavButton" class="h-[calc(var(--topnav-h)_-_1px)] px-2 m-0 text-gray-900 bg-inherit shadow-none md:hidden hover:!bg-inherit hover:!text-nwoods-accent hover:!shadow-none" aria-label="Navigation"> <svg class="h-7 w-7 block" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"> <path d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" stroke-linecap="round" stroke-linejoin="round"/> </svg> </button> <div id="topnavList" class="hidden md:block"> <div class="absolute right-0 z-30 flex flex-col items-end rounded border border-gray-200 p-4 pl-12 shadow bg-white text-gray-900 font-semibold md:flex-row md:space-x-4 md:items-start md:border-0 md:p-0 md:shadow-none md:bg-inherit"> <a href="../learn/">Learn</a> <a href="../samples/">Samples</a> <a href="../intro/">Intro</a> <a href="../api/">API</a> <a href="../download.html">Download</a> <a href="https://forum.nwoods.com/c/gojs/11" target="_blank" rel="noopener">Forum</a> <a id="tc" href="https://nwoods.com/contact.html" target="_blank" rel="noopener" onclick="getOutboundLink('https://nwoods.com/contact.html', 'contact');">Contact</a> <a id="tb" href="https://nwoods.com/sales/index.html" target="_blank" rel="noopener" onclick="getOutboundLink('https://nwoods.com/sales/index.html', 'buy');">Buy</a> </div> </div> </div> </div> </nav> <script> window.addEventListener("DOMContentLoaded", function () { // topnav var topButton = document.getElementById("topnavButton"); var topnavList = document.getElementById("topnavList"); if (topButton && topnavList) { topButton.addEventListener("click", function (e) { topnavList .classList .toggle("hidden"); e.stopPropagation(); }); document.addEventListener("click", function (e) { // if the clicked element isn't the list, close the list if (!topnavList.classList.contains("hidden") && !e.target.closest("#topnavList")) { topButton.click(); } }); // set active <a> element var url = window .location .href .toLowerCase(); var aTags = topnavList.getElementsByTagName('a'); for (var i = 0; i < aTags.length; i++) { var lowerhref = aTags[i] .href .toLowerCase(); if (lowerhref.endsWith('.html')) lowerhref = lowerhref.slice(0, -5); if (url.startsWith(lowerhref)) { aTags[i] .classList .add('active'); break; } } } }); </script> <div class="flex flex-col prose"> <div class="w-full max-w-screen-xl mx-auto"> <!-- * * * * * * * * * * * * * --> <!-- Start of GoJS sample code --> <script src="https://cdn.jsdelivr.net/npm/gojs@3.1.0"></script> <div id="allSampleContent" class="p-4 w-full"> <script src="../extensions/Figures.js"></script> <script id="code"> function init() { const FONT = 'bold 13px InterVariable, sans-serif'; go.Shape.defineFigureGenerator('Wave', (shape, w, h) => { const geo = new go.Geometry(); const fig = new go.PathFigure(0, 0, true); const param1 = shape?.parameter1 ?? 0; geo.add(fig); // w -= 1; // remove slight overlap fig.add(new go.PathSegment(go.SegmentType.Line, w, 0)); fig.add(new go.PathSegment(go.SegmentType.Line, w, h*3/4)); fig.add(new go.PathSegment(go.SegmentType.QuadraticBezier, 0, h*3/4, w/2, h/4 + h*param1)); fig.add(new go.PathSegment(go.SegmentType.Line, 0, 0)); return geo; }); myDiagram = new go.Diagram('myDiagramDiv', { 'grid.visible': true, 'grid.gridCellSize': new go.Size(30, 20), 'draggingTool.isGridSnapEnabled': true, 'resizingTool.isGridSnapEnabled': true, 'rotatingTool.snapAngleMultiple': 90, 'rotatingTool.snapAngleEpsilon': 45, 'undoManager.isEnabled': true, ModelChanged: e => { if (e.isTransactionFinished) updateAnimation(); } }); // when the document is modified, add a "*" to the title and enable the "Save" button myDiagram.addDiagramListener('Modified', e => { const button = document.getElementById('SaveButton'); if (button) button.disabled = !myDiagram.isModified; const idx = document.title.indexOf('*'); if (myDiagram.isModified) { if (idx < 0) document.title += '*'; } else { if (idx >= 0) document.title = document.title.slice(0, idx); } }); function makeDistillationColumnPlatePanel(alignment, side) { return new go.Panel('Graduated', { alignment: new go.Spot(alignment, 0), alignmentFocus: new go.Spot(alignment, 0), }) .bind('height', 'height', value => value * 0.85) .add( new go.Shape({ geometryString: "M0 0 V400", strokeWidth: 0 }) .bind('geometryString', 'height', value => `M0 0 V${value}`), new go.Shape({ interval: 1, geometryString: "M0 0 V40", graduatedSkip: n => Boolean(n % 20) ^ side, stroke: 'black', strokeDashArray: [10, 1] }) .bind('geometryString', 'width', value => `M0 0 V${value * 0.8}`), ); } function makeMetalBrush() { let color = '#fff'; return new go.Brush('Linear', { 0: go.Brush.darken(color), 0.2: color, 0.33: go.Brush.lighten(color), 0.5: color, 1: go.Brush.darken(color), start: go.Spot.Left, end: go.Spot.Right }); } function makeWaveBrush() { return new go.Brush('Linear', { 0: 'rgba(163, 183, 202, 1)', 0.9: 'rgba(209, 219, 228, 1)', 1: 'rgba(209, 219, 228, 1)', start: go.Spot.Top, end: go.Spot.Bottom }); } var WAVE_WIDTH = 50; var WAVE_HEIGHT = 10; var ORIGINAL_WIDTH = 100 * WAVE_WIDTH; myDiagram.nodeTemplateMap.add('DistillationColumn', new go.Node('Spot', { selectionObjectName: 'CAPSULE', resizable: true, resizeObjectName: 'CAPSULE', locationSpot: go.Spot.Center }) .bindTwoWay('location', 'pos', go.Point.parse, go.Point.stringify) .add( new go.Shape('Capsule', { fill: makeMetalBrush(), stroke: 'black', strokeWidth: 1, }) .bind('height', 'height', value => value + 1) .bind('width', 'width', value => value + 1), new go.Panel('Spot', {isClipping: true}) .add( new go.Shape('Capsule', { name: "CAPSULE", strokeWidth: 0, }) .bindTwoWay('height') .bindTwoWay('width'), new go.Panel('Spot') .bind('height') .bind('width') .add( new go.Shape({fill: 'white', strokeWidth: 0}), new go.Panel('Vertical', { alignmentFocus: go.Spot.BottomCenter, alignment: go.Spot.BottomCenter, stretch: go.Stretch.Horizontal }) .add( new go.Panel('Graduated', { background: 'transparent', graduatedMin: 0, graduatedMax: 100, graduatedTickBase: 0, graduatedTickUnit: 1, width: ORIGINAL_WIDTH, margin: 0, stretch: go.Stretch.Horizontal, name: 'WAVE_GRADUATED_PANEL' }) .add( new go.Shape({ name: 'line', geometryString: 'M0 0 H-' + ORIGINAL_WIDTH, stroke: 'gray', strokeWidth: 0 }), new go.Shape('Wave', { interval: 1, parameter1: 0, name: 'WAVE1', fill: makeWaveBrush(), strokeWidth: 0, desiredSize: new go.Size(WAVE_WIDTH + 1, WAVE_HEIGHT), graduatedSkip: n => n % 2 }), new go.Shape('Wave', { interval: 1, parameter1: 1, name: 'WAVE2', graduatedSkip: n => !(n % 2), desiredSize: new go.Size(WAVE_WIDTH + 1, WAVE_HEIGHT), fill: makeWaveBrush(), strokeWidth: 0 }) ), new go.Shape({ fill: new go.Brush('Linear', { 0: 'rgba(163, 183, 202, 1)', 0.25: 'rgba(117, 147, 175, 1)', 0.5: 'rgba(71, 111, 149, 1)', 0.75: 'rgba(25, 74, 122, 1)', start: go.Spot.Top, end: go.Spot.Bottom }), margin: new go.Margin(-1, 0, 0, 0), strokeWidth: 0, stretch: go.Stretch.Horizontal }) .bind('height', '', data => { let fill_height = data.fillLevel * data.height; return fill_height - (WAVE_HEIGHT / 2); }) ) ), makeDistillationColumnPlatePanel(0, true), makeDistillationColumnPlatePanel(1, false), ), new go.Panel('Auto') .add( new go.Shape({fill: 'rgba(255, 255, 255, 0.9)', stroke: 'black'}), new go.TextBlock('test', {stroke: 'black', margin: 3, font: FONT}) .bind('text') ) ) ); myDiagram.nodeTemplateMap.add('Tank', new go.Node('Spot', { selectionObjectName: 'CAPSULE', resizable: true, resizeObjectName: 'CAPSULE', locationSpot: go.Spot.Center }) .bindTwoWay('location', 'pos', go.Point.parse, go.Point.stringify) .add( new go.Shape('Capsule', { fill: makeMetalBrush(), stroke: 'black', strokeWidth: 1, }) .bind('height', 'height', value => value + 1) .bind('width', 'width', value => value + 1), new go.Panel('Spot', {isClipping: true}) .add( new go.Shape('Capsule', { name: "CAPSULE", strokeWidth: 0, }) .bindTwoWay('height') .bindTwoWay('width'), new go.Panel('Spot') .bind('height') .bind('width') .add( new go.Shape({fill: 'white', strokeWidth: 0}), new go.Panel('Vertical', { alignmentFocus: go.Spot.BottomCenter, alignment: go.Spot.BottomCenter, stretch: go.Stretch.Horizontal }) .add( new go.Panel('Graduated', { background: 'transparent', graduatedMin: 0, graduatedMax: 100, graduatedTickBase: 0, graduatedTickUnit: 1, width: ORIGINAL_WIDTH, margin: 0, stretch: go.Stretch.Horizontal, name: 'WAVE_GRADUATED_PANEL' }) .add( new go.Shape({ name: 'line', geometryString: 'M0 0 H-' + ORIGINAL_WIDTH, stroke: 'gray', strokeWidth: 0 }), new go.Shape('Wave', { interval: 1, parameter1: 0, name: 'WAVE1', fill: makeWaveBrush(), strokeWidth: 0, desiredSize: new go.Size(WAVE_WIDTH + 1, WAVE_HEIGHT), graduatedSkip: n => n % 2 }), new go.Shape('Wave', { interval: 1, parameter1: 1, name: 'WAVE2', graduatedSkip: n => !(n % 2), desiredSize: new go.Size(WAVE_WIDTH + 1, WAVE_HEIGHT), fill: makeWaveBrush(), strokeWidth: 0 }) ), new go.Shape({ fill: new go.Brush('Linear', { 0: 'rgba(163, 183, 202, 1)', 0.25: 'rgba(117, 147, 175, 1)', 0.5: 'rgba(71, 111, 149, 1)', 0.75: 'rgba(25, 74, 122, 1)', start: go.Spot.Top, end: go.Spot.Bottom }), margin: new go.Margin(-1, 0, 0, 0), strokeWidth: 0, stretch: go.Stretch.Horizontal }) .bind('height', '', data => { let fill_height = data.fillLevel * data.height; return fill_height - (WAVE_HEIGHT / 2); }) ) ), ), new go.Panel('Auto') .add( new go.Shape({fill: 'rgba(255, 255, 255, 0.9)', stroke: 'black'}), new go.TextBlock('test', {stroke: 'black', margin: 3, font: FONT}) .bind('text') ) ) ); myDiagram.nodeTemplateMap.add('Pump', new go.Node('Vertical') .bindTwoWay('location', 'pos', go.Point.parse, go.Point.stringify) .add( new go.Panel('Vertical', {portId: ''}) .add( new go.Shape('Circle', { desiredSize: new go.Size(25, 25), fill: makeMetalBrush(), strokeWidth: 1, margin: new go.Margin(0, 0, -2, 0) }), new go.Shape({ desiredSize: new go.Size(30, 8), fill: makeMetalBrush(), strokeWidth: 1 }) ), new go.TextBlock({ alignment: go.Spot.Center, textAlign: 'center', margin: 5, editable: true, font: FONT }) .bindTwoWay('text'), ) ); myDiagram.nodeTemplateMap.add('Condenser', new go.Node('Vertical') .bindTwoWay('location', 'pos', go.Point.parse, go.Point.stringify) .add( new go.Panel('Spot') .add( new go.Shape('Circle', { desiredSize: new go.Size(32, 32), fill: makeMetalBrush(), strokeWidth: 1, portId: '', fromSpot: go.Spot.Right, toSpot: go.Spot.Left }), new go.Shape({ geometryString: 'F M0 36 L0 40 4 40 0 40 20 16 20 24 40 0', desiredSize: new go.Size(35, 35), strokeWidth: 1, fill: makeMetalBrush() }) ), new go.TextBlock({ alignment: go.Spot.Center, textAlign: 'center', margin: 5, editable: true, font: FONT }) .bindTwoWay('text') ) ); myDiagram.nodeTemplateMap.add('Valve', new go.Node('Vertical', { locationSpot: new go.Spot(0.5, 0.3333), locationObjectName: 'SHAPE', selectionObjectName: 'SHAPE', rotatable: true }) .bindTwoWay('angle') .bindTwoWay('location', 'pos', go.Point.parse, go.Point.stringify) .add( new go.TextBlock({ alignment: go.Spot.Center, textAlign: 'center', margin: 5, editable: true, font: FONT }) .bindTwoWay('text') // keep the text upright, even when the whole node has been rotated upside down .bindObject('angle', 'angle', a => a === 180 ? 180 : 0), new go.Shape({ name: 'SHAPE', geometryString: 'F1 M0 0 L40 20 40 0 0 20z M20 10 L20 30 M12 30 L28 30', strokeWidth: 1, fill: makeMetalBrush(), portId: '', fromSpot: new go.Spot(1, 0.3333), toSpot: new go.Spot(0, 0.3333) }) ) ); myDiagram.nodeTemplateMap.add('Boiler', new go.Node('Vertical') .bindTwoWay('location', 'pos', go.Point.parse, go.Point.stringify) .add( new go.Panel('Spot') .add( new go.Shape('Circle', { desiredSize: new go.Size(32, 32), portId: '', fill: makeMetalBrush(), strokeWidth: 1 }), new go.Shape("Circle", { desiredSize: new go.Size(16, 16), strokeWidth: 1, alignment: go.Spot.BottomCenter, alignmentFocus: go.Spot.BottomCenter, fill: makeMetalBrush() }) ), new go.TextBlock({ alignment: go.Spot.Center, textAlign: 'center', margin: 5, editable: true, font: FONT }) .bindTwoWay('text') ) ); myDiagram.nodeTemplateMap.add('Label', new go.Node('Auto') .bindTwoWay('location', 'pos', go.Point.parse, go.Point.stringify) .add( new go.Shape({fill: 'white', stroke: 'black'}), new go.TextBlock('test', {stroke: 'black', margin: 3, font: FONT}) .bind('text') ) ); myDiagram.linkTemplate = new go.Link({ routing: go.Routing.AvoidsNodes, curve: go.Curve.JumpGap, corner: 10, reshapable: true, toShortLength: 7 }) .bindTwoWay('points') .add( // mark each Shape to get the link geometry with isPanelMain: true new go.Shape({ isPanelMain: true, stroke: 'black', strokeWidth: 5 }), new go.Shape({ isPanelMain: true, stroke: '#aaa', strokeWidth: 3 }) .bind('stroke'), new go.Shape({ isPanelMain: true, stroke: 'white', strokeWidth: 3, name: 'PIPE', strokeDashArray: [10, 10] }), new go.Shape({ toArrow: 'Triangle', fill: 'white', stroke: 'black' }), new go.Panel('Auto', { visible: false }) .bind('visible', 'text', value => value && value != '') .add( new go.Shape({ fill: 'rgba(255, 255, 255, 0.9)', stroke: 'black', strokeDashArray: [5, 5] }), new go.TextBlock({ stroke: 'black', margin: 3, font: FONT }) .bind('text') ) ); load(); } var myAnimation = null; var myWavesAnimation = null; var myWavesOffsetAnimation = null; //parameter1 isn't something you can animate by default :( //see documentation for more details: https://gojs.net/latest/api/symbols/AnimationManager.html#defineAnimationEffect //also see the intro page on animations: https://gojs.net/latest/intro/animation.html go.AnimationManager.defineAnimationEffect('waves', (obj, startValue, endValue, easing, currentTime, duration, animation) => { let value = easing(currentTime, startValue, endValue - startValue, duration); obj.parameter1 = value; }); go.AnimationManager.defineAnimationEffect('offset', (obj, startValue, endValue, easing, currentTime, duration, animation) => { let value = easing(currentTime, startValue, endValue - startValue, duration); obj.alignment = new go.Spot(0, 0, value, 0); }); function updateAnimation() { //for the flow in the pipes if (myAnimation) myAnimation.stop(); // Animate the flow in the pipes myAnimation = new go.Animation(); myAnimation.easing = go.Animation.EaseLinear; myDiagram.links.each(link => myAnimation.add(link.findObject('PIPE'), 'strokeDashOffset', 20, 0) ); // Run indefinitely myAnimation.runCount = Infinity; myAnimation.start(); //for the waves movement if (myWavesAnimation) myWavesAnimation.stop(); myWavesAnimation = new go.Animation(); myWavesAnimation.easing = go.Animation.EaseInOutQuad; myWavesAnimation.reversible = true; myWavesAnimation.duration = 2000; myDiagram.nodes.each(node => { let wave1 = node.findObject('WAVE1'); let wave2 = node.findObject('WAVE2'); if (wave1) myWavesAnimation.add(wave1, 'waves', 0, 1); if (wave2) myWavesAnimation.add(wave2, 'waves', 1, 0); }); myWavesAnimation.runCount = Infinity; myWavesAnimation.start(); //waves offset movement if (myWavesOffsetAnimation) myWavesOffsetAnimation.stop(); myWavesOffsetAnimation = new go.Animation(); myWavesOffsetAnimation.easing = go.Animation.EaseInOutQuad; myWavesOffsetAnimation.reversible = true; myWavesOffsetAnimation.duration = 5000; myDiagram.nodes.each(node => { let waveGraduatedPanel = node.findObject('WAVE_GRADUATED_PANEL'); if (waveGraduatedPanel) myWavesOffsetAnimation.add(waveGraduatedPanel, 'offset', -30, 0); }); myWavesOffsetAnimation.runCount = Infinity; myWavesOffsetAnimation.start(); } function save() { document.getElementById('mySavedModel').value = myDiagram.model.toJson(); myDiagram.isModified = false; } function load() { myDiagram.model = go.Model.fromJson(document.getElementById('mySavedModel').value); } window.addEventListener('DOMContentLoaded', init); </script> <div id="sample"> <div id="myDiagramDiv" style="border: solid 1px black; width: 100%; height: 500px"></div> <p> A <em>process flow diagram</em> is commonly used in chemical and process engineering to indicate the general flow of plant processes and equipment. A simple SCADA diagram, with animation of the flow along the pipes, is implemented here. It shows a basic distillation process — more information available on <a href='https://en.wikipedia.org/wiki/Reflux'>Wikipedia</a>. </p> <p> The diagram displays the background grid layer by setting <b>grid.visible</b> to true, and also allows snapping to the grid using <a>DraggingTool.isGridSnapEnabled</a>, <a>ResizingTool.isGridSnapEnabled</a>, and <a>RotatingTool.snapAngleMultiple</a> alongside <a>RotatingTool.snapAngleEpsilon</a>. It also makes use of <a href="../intro/graduatedPanels.html">Graduated Panels</a> for the water effect inside the tanks. </p> <p> There are two custom animations in this diagram: one that modifies the <a>Shape.strokeDashOffset</a> for each link and one that uses <a>AnimationManager.defineAnimationEffect</a> to animate the waves in the tanks. </p> <div> <div> <button id="SaveButton" onclick="save()">Save</button> <button onclick="load()">Load</button> Diagram Model saved in JSON format: </div> <textarea id="mySavedModel" style="width: 100%; height: 300px"> { "class": "GraphLinksModel", "pointsDigits": 1, "linkFromPortIdProperty": "fromPort", "linkToPortIdProperty": "toPort", "nodeDataArray": [ {"key":"Tank1","category":"Tank","pos":"-30 200","text":"Feed Tank","height":100,"width":200,"fillLevel":"0.8"}, {"key":"Valve1","category":"Valve","pos":"150 200","text":"Valve","angle":0,"isOn":true}, {"key":"Tank2","category":"DistillationColumn","pos":"300 200","text":"Distillation Column","height":275,"width":75,"fillLevel":"0.15"}, {"key":"Condenser1","category":"Condenser","pos":"390 0","text":"Condenser","isOn":true}, {"key":"Tank3","category":"Tank","pos":"600 20","text":"Reflux Drum","height":75,"width":150,"fillLevel":"0.1"}, {"key":"Pump1","category":"Pump","pos":"550 100","text":"Pump","isOn":true}, {"key":"Boiler1","category":"Boiler","pos":"390 360","text":"Reboiler","isOn":true}, {"key":"Valve2","category":"Valve","pos":"480 260","text":"Valve","angle":270,"isOn":true}, {"key":"Tank4","category":"Tank","pos":"750 180","text":"Holding Tank","height":75,"width":120,"fillLevel":"0.8"}, {"key":"Tank5","category":"Tank","pos":"750 380","text":"Holding Tank","height":75,"width":120,"fillLevel":"0.8"} ], "linkDataArray": [ {"from":"Tank1","to":"Valve1","stroke":"rgba(117, 147, 175, 0.5)","points":[71.0,200.0,81.0,200.0,100.3,200.0,100.3,200.0,119.5,200.0,129.5,200.0]}, {"from":"Valve1","to":"Tank2","stroke":"rgba(117, 147, 175, 0.5)","points":[170.5,200.0,180.5,200.0,203.5,200.0,203.5,200.0,226.5,200.0,236.5,200.0]}, {"from":"Tank2","to":"Condenser1","points":[300.0,61.5,300.0,51.5,300.0,52.0,300.0,52.0,300.0,18.0,402.5,18.0,412.5,18.0]}, {"from":"Condenser1","to":"Tank3","stroke":"rgba(117, 147, 175, 0.5)","points":[446.5,18.0,456.5,18.0,458.0,18.0,458.0,18.0,514.0,18.0,524.0,20.0]}, {"from":"Tank3","to":"Pump1","stroke":"rgba(117, 147, 175, 0.5)","points":[600.0,58.5,600.0,68.5,600.0,79.3,570.6,79.3,570.6,90.0,570.6,100.0]}, {"from":"Pump1","to":"Tank2","stroke":"rgba(117, 147, 175, 0.5)", "points":[561.0,116.5,551.0,116.5,548.0,116.5,548.0,66.4,333.6,66.4,326.5,73.5]}, {"from":"Pump1","to":"Tank4","stroke":"rgba(117, 147, 175, 0.5)","text":"Overhead product","points":[586.0,116.5,596.0,116.5,596.0,116.5,596.0,180.0,679.0,180.0,689.0,180.0]}, {"from":"Tank2","to":"Boiler1","stroke":"rgba(117, 147, 175, 0.5)", "points":[300.0,338.5,300.0,348.5,300.0,349.2,422.0,349.2,422.0,350.0,422.0,360.0]}, {"from":"Boiler1","to":"Valve2","points":[422.0,360.0,422.0,350.0,422.0,320.3,480.0,320.3,480.0,290.5,480.0,280.5]}, {"from":"Valve2","to":"Tank2","points":[480.0,239.5,480.0,229.5,480.0,200.0,426.8,200.0,373.5,200.0,363.5,200.0]}, {"from":"Boiler1","to":"Tank5","stroke":"rgba(117, 147, 175, 0.5)","text":"Bottom product\n(condensate from reboiler)","points":[438.5,376.5,448.5,376.5,455.0,376.5,455.0,376.5,679.0,376.5,689.0,380.0]} ]} </textarea> </div> </div> </div> <!-- * * * * * * * * * * * * * --> <!-- End of GoJS sample code --> </div> <div id="allTagDescriptions" class="p-4 w-full max-w-screen-xl mx-auto"> <hr/> <h3 class="text-xl">GoJS Features in this sample</h3> <!-- blacklist tags that do not correspond to a specific GoJS feature --> <h4>Geometry Path Strings</h4> <p> The <b>GoJS</b> <a href="../api/symbols/Geometry.html" target="api">Geometry</a> class controls the "shape" of a <a href="../api/symbols/Shape.html" target="api">Shape</a>, whereas the <a href="../api/symbols/Shape.html#fill" target="api">Shape.fill</a> and <a href="../api/symbols/Shape.html#stroke" target="api">Shape.stroke</a> and other shape properties control the colors and appearance of the shape. For common shape figures, there are predefined geometries that can be used by setting <a href="../api/symbols/Shape.html#figure" target="api">Shape.figure</a>. However one can also define custom geometries. </p> <p> One can construct any Geometry by allocating and initializing a <a href="../api/symbols/Geometry.html" target="api">Geometry</a> of at least one <a href="../api/symbols/PathFigure.html" target="api">PathFigure</a> holding some <a href="../api/symbols/PathSegment.html" target="api">PathSegment</a>s. But you may find that using the string representation of a Geometry is easier to write and save in a database. Use the static method <a href="../api/symbols/Geometry.html#parse" target="api">Geometry.parse</a> or the <a href="../api/symbols/Shape.html#geometryString" target="api">Shape.geometryString</a> property to transform a geometry path string into a <a href="../api/symbols/Geometry.html" target="api">Geometry</a> object. </p> <p> More information can be found in the <a href="../intro/geometry.html">GoJS Intro</a>. </p> <p> <a href="../samples/index.html#geometries">Related samples</a> </p> <hr> <!-- blacklist tags that do not correspond to a specific GoJS feature --> <h4>Grid Patterns</h4> <p> <b>GoJS</b> provides functionality to display a grid of lines drawn at regular intervals. Grid Panels can also force dragged parts to be aligned on grid points, and resize parts to be multiples of the grid cell size. </p> <p> Grids are implemented using a type of <a href="../api/symbols/Panel.html" target="api">Panel</a>, <a href="../api/symbols/PanelLayout.html#Grid" target="api">Panel.Grid</a>. Grid Panels, like most other types of Panels, can be used within <a href="../api/symbols/Node.html" target="api">Node</a>s or any other kind of <a href="../api/symbols/Part.html" target="api">Part</a>. However when they are used as the <a href="../api/symbols/Diagram.html#grid" target="api">Diagram.grid</a>, they are effectively infinite in extent. </p> <p> More information can be found in the <a href="../intro/grids.html">GoJS Intro</a>. </p> <p> <a href="../samples/index.html#grid">Related samples</a> </p> <hr> <!-- blacklist tags that do not correspond to a specific GoJS feature --> <!-- blacklist tags that do not correspond to a specific GoJS feature --> <!-- blacklist tags that do not correspond to a specific GoJS feature --> <h4>Animation</h4> <p> <b>GoJS</b> offers several built-in animations, enabled by default, as well as the ability to create arbitrary animations. </p> <p> The <a href="../api/symbols/Diagram.html#animationManager" target="api">Diagram.animationManager</a> handles animations within a <a href="../api/symbols/Diagram.html" target="api">Diagram</a>. The <a href="../api/symbols/AnimationManager.html" target="api">AnimationManager</a> automatically sets up and dispatches default animations, and has properties to customize and disable them. Custom animations are possible by creating instances of <a href="../api/symbols/Animation.html" target="api">Animation</a> or <a href="../api/symbols/AnimationTrigger.html" target="api">AnimationTrigger</a>. More information can be found in the <a href="../intro/animation.html">GoJS Intro</a>. </p> <p> <a href="../samples/index.html#animation">Related samples</a> </p> <hr> <!-- blacklist tags that do not correspond to a specific GoJS feature --> <h4>SCADA Diagrams</h4> <p> SCADA (supervisory control and data acquisition) diagrams are used to display, control, and supervise machines and processes. GoJS is used worldwide to build human-machine interface diagrams in monitoring and control software. GoJS SCADA applications include power plant and refinery monitoring, heavy industry management, building security monitoring, and more. </p> <p> <a href="../samples/index.html#scada">Related samples</a> </p> <hr> </div> </div> </body> <!-- This script is part of the gojs.net website, and is not needed to run the sample --> <script src="../assets/js/goSamples.js"></script> </html>