1 // =================================
  2 // Copyright (c) 2020 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 #include <gendoc/html/ClassInheritanceDiagramCode.hpp>
  7 
  8 namespace gendoc { namespace html {
  9 
 10 void GenerateClassInheritanceDiagramCode(CodeFormatter& formatter)
 11 {
 12     formatter.WriteLine("// JavaScript source code for drawing class inheritance diagrams");
 13     formatter.WriteLine();
 14 
 15     formatter.WriteLine("function pick(level, classes) {");
 16     formatter.IncIndent();
 17     formatter.WriteLine("var levelClasses = [];");
 18     formatter.WriteLine("var n = classes.length;");
 19     formatter.WriteLine("for (var i = 0; i < n; ++i) {");
 20     formatter.IncIndent();
 21     formatter.WriteLine("var c = classes[i];");
 22     formatter.WriteLine("if (c.level == level) {");
 23     formatter.IncIndent();
 24     formatter.WriteLine("levelClasses.push(c);");
 25     formatter.DecIndent();
 26     formatter.WriteLine("}");
 27     formatter.DecIndent();
 28     formatter.WriteLine("}");
 29     formatter.WriteLine("return levelClasses;");
 30     formatter.DecIndent();
 31     formatter.WriteLine("}");
 32     formatter.WriteLine();
 33 
 34     formatter.WriteLine("function createClassElements(levelClasses, maxTextWidth, maxTextHeight) {");
 35     formatter.IncIndent();
 36     formatter.WriteLine("var textDimensions = { width: maxTextWidth, height: maxTextHeight };");
 37     formatter.WriteLine("var n = levelClasses.length;");
 38     formatter.WriteLine("for (var i = 0; i < n; ++i) {");
 39     formatter.IncIndent();
 40     formatter.WriteLine("var levelClass = levelClasses[i];");
 41     formatter.WriteLine("var svg = document.getElementById(\"classInheritanceDiagram\");");
 42     formatter.WriteLine("var rectElement = document.createElementNS('http://www.w3.org/2000/svg', 'rect');");
 43     formatter.WriteLine("rectElement.setAttribute(\"id\", levelClass.id + \"_rect\");");
 44     formatter.WriteLine("var linkElement = document.createElementNS('http://www.w3.org/2000/svg', 'a');");
 45     formatter.WriteLine("linkElement.setAttribute(\"href\", levelClass.link);");
 46     formatter.WriteLine("var textElement = document.createElementNS('http://www.w3.org/2000/svg', 'text');");
 47     formatter.WriteLine("linkElement.appendChild(textElement);");
 48     formatter.WriteLine("textElement.setAttribute(\"id\", levelClass.id + \"_text\");");
 49     formatter.WriteLine("textElement.innerHTML = levelClass.name;");
 50     formatter.WriteLine("svg.appendChild(rectElement);");
 51     formatter.WriteLine("svg.appendChild(linkElement);");
 52     formatter.WriteLine("var bb = textElement.getBBox();");
 53     formatter.WriteLine("var textWidth = bb.width;");
 54     formatter.WriteLine("var textHeight = bb.height;");
 55     formatter.WriteLine("levelClass.textWidth = textWidth;");
 56     formatter.WriteLine("levelClass.textHeight = textHeight;");
 57     formatter.WriteLine("if (textWidth > textDimensions.width) {");
 58     formatter.IncIndent();
 59     formatter.WriteLine("textDimensions.width = textWidth;");
 60     formatter.DecIndent();
 61     formatter.WriteLine("}");
 62     formatter.WriteLine("if (textHeight > textDimensions.height) {");
 63     formatter.IncIndent();
 64     formatter.WriteLine("textDimensions.height = textHeight;");
 65     formatter.DecIndent();
 66     formatter.WriteLine("}");
 67     formatter.DecIndent();
 68     formatter.WriteLine("}");
 69     formatter.WriteLine("return textDimensions;");
 70     formatter.DecIndent();
 71     formatter.WriteLine("}");
 72     formatter.WriteLine();
 73 
 74     formatter.WriteLine("function drawDiagram(classes) {");
 75     formatter.IncIndent();
 76     formatter.WriteLine("var cont = true;");
 77     formatter.WriteLine("var level = 0;");
 78     formatter.WriteLine("var yspace = 8;");
 79     formatter.WriteLine("var xspace = 8;");
 80     formatter.WriteLine("var minRectWidth = 100;");
 81     formatter.WriteLine("var minRectHeight = 40;");
 82     formatter.WriteLine("var maxTextWidth = 0;");
 83     formatter.WriteLine("var maxTextHeight = 0;");
 84     formatter.WriteLine("var triangleHeight = 20;");
 85     formatter.WriteLine("var triangleWidth = 20;");
 86     formatter.WriteLine("var targetHandleHeight = 20;");
 87     formatter.WriteLine("var sourceHandleHeight = 40;");
 88     formatter.WriteLine("var rectXSpace = 20;");
 89     formatter.WriteLine("var horizontalRectYSpace = triangleHeight + targetHandleHeight + sourceHandleHeight;");
 90     formatter.WriteLine("var verticalRectYSpace = 20;");
 91     formatter.WriteLine("var derivedTriangleHeight = 8;");
 92     formatter.WriteLine("var derivedTriangleWidth = 8;");
 93     formatter.WriteLine("var widthThreshold = 1800;");
 94     formatter.WriteLine("var allLevelClasses = [];");
 95     formatter.WriteLine("while (cont) {");
 96     formatter.IncIndent();
 97     formatter.WriteLine("var levelClasses = pick(level, classes);");
 98     formatter.WriteLine("var n = levelClasses.length;");
 99     formatter.WriteLine("cont = n > 0;");
100     formatter.WriteLine("if (cont) {");
101     formatter.IncIndent();
102     formatter.WriteLine("var textDimensions = createClassElements(levelClasses, maxTextWidth, maxTextHeight);");
103     formatter.WriteLine("if (textDimensions.width > maxTextWidth) {");
104     formatter.IncIndent();
105     formatter.WriteLine("maxTextWidth = textDimensions.width;");
106     formatter.DecIndent();
107     formatter.WriteLine("}");
108     formatter.WriteLine("if (textDimensions.height > maxTextHeight) {");
109     formatter.IncIndent();
110     formatter.WriteLine("maxTextHeight = textDimensions.height;");
111     formatter.DecIndent();
112     formatter.WriteLine("}");
113     formatter.WriteLine("allLevelClasses.push(levelClasses);");
114     formatter.WriteLine("++level;");
115     formatter.DecIndent();
116     formatter.WriteLine("}");
117     formatter.DecIndent();
118     formatter.WriteLine("}");
119     formatter.WriteLine("var maxLevel = level;");
120     formatter.WriteLine("var rectWidth = Math.max(minRectWidth, maxTextWidth + 2 * xspace);");
121     formatter.WriteLine("var rectHeight = Math.max(minRectHeight, maxTextHeight + 2 * yspace);");
122     formatter.WriteLine("var totalWidth = 0;");
123     formatter.WriteLine("var totalHeight = 0;");
124     formatter.WriteLine("var horizontalDirection = 0;");
125     formatter.WriteLine("var verticalDirection = 1;");
126     formatter.WriteLine("var levelDirection = [];");
127     formatter.WriteLine("var levelHeight = [];");
128     formatter.WriteLine("var prevW = 0;");
129     formatter.WriteLine("for (level = 0; level < maxLevel; ++level) {");
130     formatter.IncIndent();
131     formatter.WriteLine("var levelClasses = allLevelClasses[level];");
132     formatter.WriteLine("var n = levelClasses.length;");
133     formatter.WriteLine("var w = n * (rectWidth + rectXSpace);");
134     formatter.WriteLine("var h = rectHeight + horizontalRectYSpace;");
135     formatter.WriteLine("if (w < widthThreshold) {");
136     formatter.IncIndent();
137     formatter.WriteLine("levelDirection.push(horizontalDirection);");
138     formatter.WriteLine("if (w > totalWidth) {");
139     formatter.IncIndent();
140     formatter.WriteLine("totalWidth = w;");
141     formatter.DecIndent();
142     formatter.WriteLine("}");
143     formatter.DecIndent();
144     formatter.WriteLine("}");
145     formatter.WriteLine("else {");
146     formatter.IncIndent();
147     formatter.WriteLine("w = prevW + rectWidth + rectXSpace;");
148     formatter.WriteLine("h = n * (rectHeight + verticalRectYSpace);");
149     formatter.WriteLine("levelDirection.push(verticalDirection);");
150     formatter.WriteLine("totalWidth += w;");
151     formatter.DecIndent();
152     formatter.WriteLine("}");
153     formatter.WriteLine("totalHeight += h;");
154     formatter.WriteLine("levelHeight.push(h);");
155     formatter.WriteLine("prevW = w;");
156     formatter.DecIndent();
157     formatter.WriteLine("}");
158     formatter.WriteLine("var svg = document.getElementById(\"classInheritanceDiagram\");");
159     formatter.WriteLine("svg.setAttribute(\"width\", totalWidth.toString());");
160     formatter.WriteLine("svg.setAttribute(\"height\", totalHeight.toString());");
161     formatter.WriteLine("var prevRectY = 0;");
162     formatter.WriteLine("var prevRectX = 0;");
163     formatter.WriteLine("var prevHandleX2 = -1;");
164     formatter.WriteLine("var prevHandleY2 = -1;");
165     formatter.WriteLine("var prevY = 0;");
166     formatter.WriteLine("for (level = 0; level < maxLevel; ++level) {");
167     formatter.IncIndent();
168     formatter.WriteLine("var direction = levelDirection[level];");
169     formatter.WriteLine("var levelClasses = allLevelClasses[level];");
170     formatter.WriteLine("var n = levelClasses.length;");
171     formatter.WriteLine("var rectY = prevY;");
172     formatter.WriteLine("prevY += levelHeight[level];");
173     formatter.WriteLine("var rectX = (totalWidth / n - rectWidth) / 2;");
174     formatter.WriteLine("var minHandleX = Number.MAX_SAFE_INTEGER;");
175     formatter.WriteLine("var maxHandleX = 0;");
176     formatter.WriteLine("var handleY = 0;");
177     formatter.WriteLine("for (var i = 0; i < n; ++i) {");
178     formatter.IncIndent();
179     formatter.WriteLine("var levelClass = levelClasses[i];");
180     formatter.WriteLine("var textWidth = levelClass.textWidth;");
181     formatter.WriteLine("var textHeight = levelClass.textHeight;");
182     formatter.WriteLine("if (direction == horizontalDirection) {");
183     formatter.IncIndent();
184     formatter.WriteLine("rectX = (totalWidth / n - rectWidth) / 2 + i * (rectWidth + rectXSpace);");
185     formatter.DecIndent();
186     formatter.WriteLine("}");
187     formatter.WriteLine("else if (direction == verticalDirection) {");
188     formatter.IncIndent();
189     formatter.WriteLine("rectX = prevRectX + (rectWidth + rectXSpace);");
190     formatter.WriteLine("rectY = prevRectY + horizontalRectYSpace + i * (rectHeight + verticalRectYSpace);");
191     formatter.DecIndent();
192     formatter.WriteLine("}");
193     formatter.WriteLine("var textX = rectX + (rectWidth - textWidth) / 2;");
194     formatter.WriteLine("var textY = (rectY + rectHeight - yspace / 2) - (rectHeight - textHeight) / 2;");
195     formatter.WriteLine("var rectElement = document.getElementById(levelClass.id + \"_rect\");");
196     formatter.WriteLine("rectElement.setAttribute(\"x\", rectX.toString());");
197     formatter.WriteLine("rectElement.setAttribute(\"y\", rectY.toString());");
198     formatter.WriteLine("rectElement.setAttribute(\"width\", rectWidth.toString());");
199     formatter.WriteLine("rectElement.setAttribute(\"height\", rectHeight.toString());");
200     formatter.WriteLine("var fillColor = \"white\";");
201     formatter.WriteLine("if (levelClass.subject) {");
202     formatter.IncIndent();
203     formatter.WriteLine("fillColor = \"floralWhite\";");
204     formatter.DecIndent();
205     formatter.WriteLine("}");
206     formatter.WriteLine("rectElement.setAttribute(\"fill\", fillColor);");
207     formatter.WriteLine("rectElement.setAttribute(\"stroke\", \"black\");");
208     formatter.WriteLine("var textElement = document.getElementById(levelClass.id + \"_text\");");
209     formatter.WriteLine("textElement.setAttribute(\"x\", textX.toString());");
210     formatter.WriteLine("textElement.setAttribute(\"y\", textY.toString());");
211     formatter.WriteLine("if (level < maxLevel - 1) {");
212     formatter.IncIndent();
213     formatter.WriteLine("var triangleElement = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');");
214     formatter.WriteLine("var tipX = rectX + rectWidth / 2;");
215     formatter.WriteLine("var tipY = rectY + rectHeight;");
216     formatter.WriteLine("var leftX = tipX - triangleWidth / 2;");
217     formatter.WriteLine("var leftY = rectY + rectHeight + triangleHeight;");
218     formatter.WriteLine("var rightX = tipX + triangleWidth / 2;");
219     formatter.WriteLine("var rightY = rectY + rectHeight + triangleHeight;");
220     formatter.WriteLine("triangleElement.setAttribute(\"points\",");
221     formatter.IncIndent();
222     formatter.WriteLine("tipX.toString() + \",\" + tipY.toString() + \" \" +");
223     formatter.WriteLine("leftX.toString() + \",\" + leftY.toString() + \" \" +");
224     formatter.WriteLine("rightX.toString() + \",\" + rightY.toString());");
225     formatter.DecIndent();
226     formatter.WriteLine("triangleElement.setAttribute(\"fill\", \"white\");");
227     formatter.WriteLine("triangleElement.setAttribute(\"stroke\", \"black\");");
228     formatter.WriteLine("svg.appendChild(triangleElement);");
229     formatter.WriteLine("var targetHandleElement = document.createElementNS('http://www.w3.org/2000/svg', 'line');");
230     formatter.WriteLine("var handleX1 = tipX;");
231     formatter.WriteLine("var handleY1 = tipY + triangleHeight;");
232     formatter.WriteLine("var handleX2 = tipX;");
233     formatter.WriteLine("var handleY2 = tipY + triangleHeight + targetHandleHeight;");
234     formatter.WriteLine("targetHandleElement.setAttribute(\"x1\", handleX1.toString());");
235     formatter.WriteLine("targetHandleElement.setAttribute(\"y1\", handleY1.toString());");
236     formatter.WriteLine("targetHandleElement.setAttribute(\"x2\", handleX2.toString());");
237     formatter.WriteLine("targetHandleElement.setAttribute(\"y2\", handleY2.toString());");
238     formatter.WriteLine("targetHandleElement.setAttribute(\"stroke\", \"black\");");
239     formatter.WriteLine("svg.appendChild(targetHandleElement);");
240     formatter.WriteLine("prevHandleX2 = handleX1;");
241     formatter.WriteLine("prevHandleY2 = handleY1;");
242     formatter.WriteLine("if (handleX1 < minHandleX) {");
243     formatter.IncIndent();
244     formatter.WriteLine("minHandleX = handleX1;");
245     formatter.WriteLine("handleY = handleY2;");
246     formatter.DecIndent();
247     formatter.WriteLine("}");
248     formatter.WriteLine("if (handleX1 > maxHandleX) {");
249     formatter.IncIndent();
250     formatter.WriteLine("maxHandleX = handleX1;");
251     formatter.WriteLine("handleY = handleY2;");
252     formatter.DecIndent();
253     formatter.WriteLine("}");
254     formatter.DecIndent();
255     formatter.WriteLine("} else if (level == maxLevel - 1 && levelClass.hasDerivedClasses) {");
256     formatter.IncIndent();
257     formatter.WriteLine("var derivedTriangleElement = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');");
258     formatter.WriteLine("var cornerX = rectX + rectWidth;");
259     formatter.WriteLine("var cornerY = rectY + rectHeight;");
260     formatter.WriteLine("var verticalX = rectX + rectWidth;");
261     formatter.WriteLine("var verticalY = rectY + rectHeight - derivedTriangleHeight;");
262     formatter.WriteLine("var horizontalX = rectX + rectWidth - derivedTriangleWidth;");
263     formatter.WriteLine("var horizontalY = rectY + rectHeight;");
264     formatter.WriteLine("derivedTriangleElement.setAttribute(\"points\",");
265     formatter.IncIndent();
266     formatter.WriteLine("cornerX.toString() + \",\" + cornerY.toString() + \" \" +");
267     formatter.WriteLine("verticalX.toString() + \",\" + verticalY.toString() + \" \" +");
268     formatter.WriteLine("horizontalX.toString() + \",\" + horizontalY.toString());");
269     formatter.DecIndent();
270     formatter.WriteLine("derivedTriangleElement.setAttribute(\"fill\", \"black\");");
271     formatter.WriteLine("svg.appendChild(derivedTriangleElement);");
272     formatter.DecIndent();
273     formatter.WriteLine("}");
274     formatter.WriteLine("if (level > 0 && direction == horizontalDirection) {");
275     formatter.IncIndent();
276     formatter.WriteLine("var sourceHandleElement = document.createElementNS('http://www.w3.org/2000/svg', 'line');");
277     formatter.WriteLine("var handleX1 = rectX + rectWidth / 2;");
278     formatter.WriteLine("var handleY1 = rectY;");
279     formatter.WriteLine("var handleX2 = rectX + rectWidth / 2;");
280     formatter.WriteLine("var handleY2 = rectY - sourceHandleHeight;");
281     formatter.WriteLine("sourceHandleElement.setAttribute(\"x1\", handleX1.toString());");
282     formatter.WriteLine("sourceHandleElement.setAttribute(\"y1\", handleY1.toString());");
283     formatter.WriteLine("sourceHandleElement.setAttribute(\"x2\", handleX2.toString());");
284     formatter.WriteLine("sourceHandleElement.setAttribute(\"y2\", handleY2.toString());");
285     formatter.WriteLine("sourceHandleElement.setAttribute(\"stroke\", \"black\");");
286     formatter.WriteLine("svg.appendChild(sourceHandleElement);");
287     formatter.WriteLine("if (handleX1 < minHandleX) {");
288     formatter.IncIndent();
289     formatter.WriteLine("minHandleX = handleX1;");
290     formatter.WriteLine("handleY = handleY2;");
291     formatter.DecIndent();
292     formatter.WriteLine("}");
293     formatter.WriteLine("if (handleX1 > maxHandleX) {");
294     formatter.IncIndent();
295     formatter.WriteLine("maxHandleX = handleX1;");
296     formatter.WriteLine("handleY = handleY2;");
297     formatter.DecIndent();
298     formatter.WriteLine("}");
299     formatter.DecIndent();
300     formatter.WriteLine("}");
301     formatter.WriteLine("else if (level > 0 && direction == verticalDirection) {");
302     formatter.IncIndent();
303     formatter.WriteLine("var sourceHandleElement = document.createElementNS('http://www.w3.org/2000/svg', 'line');");
304     formatter.WriteLine("var handleX1 = rectX;");
305     formatter.WriteLine("var handleY1 = rectY + rectHeight / 2;");
306     formatter.WriteLine("var handleX2 = rectX - rectWidth / 2 - rectXSpace;");
307     formatter.WriteLine("var handleY2 = rectY + rectHeight / 2;");
308     formatter.WriteLine("sourceHandleElement.setAttribute(\"x1\", handleX1.toString());");
309     formatter.WriteLine("sourceHandleElement.setAttribute(\"y1\", handleY1.toString());");
310     formatter.WriteLine("sourceHandleElement.setAttribute(\"x2\", handleX2.toString());");
311     formatter.WriteLine("sourceHandleElement.setAttribute(\"y2\", handleY2.toString());");
312     formatter.WriteLine("sourceHandleElement.setAttribute(\"stroke\", \"black\");");
313     formatter.WriteLine("svg.appendChild(sourceHandleElement);");
314     formatter.WriteLine("if (prevHandleX2 != -1 && prevHandleY2 != -1) {");
315     formatter.IncIndent();
316     formatter.WriteLine("var connectorHandleElement = document.createElementNS('http://www.w3.org/2000/svg', 'line');");
317     formatter.WriteLine("connectorHandleElement.setAttribute(\"x1\", handleX2.toString());");
318     formatter.WriteLine("connectorHandleElement.setAttribute(\"y1\", handleY2.toString());");
319     formatter.WriteLine("connectorHandleElement.setAttribute(\"x2\", prevHandleX2.toString());");
320     formatter.WriteLine("connectorHandleElement.setAttribute(\"y2\", prevHandleY2.toString());");
321     formatter.WriteLine("connectorHandleElement.setAttribute(\"stroke\", \"black\");");
322     formatter.WriteLine("svg.appendChild(connectorHandleElement);");
323     formatter.DecIndent();
324     formatter.WriteLine("}");
325     formatter.WriteLine("prevHandleX2 = handleX2");
326     formatter.WriteLine("prevHandleY2 = handleY2;");
327     formatter.DecIndent();
328     formatter.WriteLine("}");
329     formatter.DecIndent();
330     formatter.WriteLine("}");
331     formatter.WriteLine("if (minHandleX < maxHandleX && direction == horizontalDirection) {");
332     formatter.IncIndent();
333     formatter.WriteLine("var hlineElement = document.createElementNS('http://www.w3.org/2000/svg', 'line');");
334     formatter.WriteLine("hlineElement.setAttribute(\"x1\", minHandleX.toString());");
335     formatter.WriteLine("hlineElement.setAttribute(\"y1\", handleY.toString());");
336     formatter.WriteLine("hlineElement.setAttribute(\"x2\", maxHandleX.toString());");
337     formatter.WriteLine("hlineElement.setAttribute(\"y2\", handleY.toString());");
338     formatter.WriteLine("hlineElement.setAttribute(\"stroke\", \"black\");");
339     formatter.WriteLine("svg.appendChild(hlineElement);");
340     formatter.DecIndent();
341     formatter.WriteLine("}");
342     formatter.WriteLine("prevRectY = rectY;");
343     formatter.WriteLine("prevRectX = rectX;");
344     formatter.DecIndent();
345     formatter.WriteLine("}");
346     formatter.DecIndent();
347     formatter.WriteLine("}");
348     formatter.WriteLine();
349 }
350 
351 } } // namespace gendoc::html