1 /**
2 * \file drawMath.c
3 * \brief outputs the math of a model as a dot graph
4 * \author Sarah Keating
5 *
6 * <!--------------------------------------------------------------------------
7 * This sample program is distributed under a different license than the rest
8 * of libSBML. This program uses the open-source MIT license, as follows:
9 *
10 * Copyright (c) 2013-2018 by the California Institute of Technology
11 * (California, USA), the European Bioinformatics Institute (EMBL-EBI, UK)
12 * and the University of Heidelberg (Germany), with support from the National
13 * Institutes of Health (USA) under grant R01GM070923. All rights reserved.
14 *
15 * Permission is hereby granted, free of charge, to any person obtaining a
16 * copy of this software and associated documentation files (the "Software"),
17 * to deal in the Software without restriction, including without limitation
18 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
19 * and/or sell copies of the Software, and to permit persons to whom the
20 * Software is furnished to do so, subject to the following conditions:
21 *
22 * The above copyright notice and this permission notice shall be included in
23 * all copies or substantial portions of the Software.
24 *
25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
28 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
30 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
31 * DEALINGS IN THE SOFTWARE.
32 *
33 * Neither the name of the California Institute of Technology (Caltech), nor
34 * of the European Bioinformatics Institute (EMBL-EBI), nor of the University
35 * of Heidelberg, nor the names of any contributors, may be used to endorse
36 * or promote products derived from this software without specific prior
37 * written permission.
38 * ------------------------------------------------------------------------ -->
39 */
40
41
42 #include <stdio.h>
43 #include <stdlib.h>
44
45 #include <sbml/util/util.h>
46 #include <sbml/SBMLTypes.h>
47
48 #include "FormulaGraphvizFormatter.h"
49
50
51 static int noClusters = 0;
52
53 FILE * fout;
54
55 /**
56 * @return the given formula AST as a directed graph. The caller
57 * owns the returned string and is responsible for freeing it.
58 */
59 char *
SBML_formulaToDot(const ASTNode_t * tree)60 SBML_formulaToDot (const ASTNode_t *tree)
61 {
62 StringBuffer_t *sb = StringBuffer_create(128);
63 char *name;
64 char *s;
65
66 if (FormulaGraphvizFormatter_isFunction(tree)
67 || ASTNode_isOperator(tree))
68 {
69 FormulaGraphvizFormatter_visit(NULL, tree, sb);
70 }
71 else
72 {
73 name = FormulaGraphvizFormatter_format(tree);
74 StringBuffer_append(sb, name);
75 }
76
77 StringBuffer_append(sb, "}\n");
78
79 s = StringBuffer_getBuffer(sb);
80 free(sb);
81
82 return s;
83 }
84
85
86 /**
87 * @return true (non-zero) if the given ASTNode is to formatted as a
88 * function.
89 */
90 int
FormulaGraphvizFormatter_isFunction(const ASTNode_t * node)91 FormulaGraphvizFormatter_isFunction (const ASTNode_t *node)
92 {
93 return
94 ASTNode_isFunction (node) ||
95 ASTNode_isLambda (node) ||
96 ASTNode_isLogical (node) ||
97 ASTNode_isRelational(node);
98 }
99
100
101 /**
102 * Formats the given ASTNode as a directed graph token and returns the result as
103 * a string.
104 */
105 char *
FormulaGraphvizFormatter_format(const ASTNode_t * node)106 FormulaGraphvizFormatter_format (const ASTNode_t *node)
107 {
108 StringBuffer_t *p = StringBuffer_create(128);
109 char *s = NULL;
110
111 if (ASTNode_isOperator(node))
112 {
113 s = FormulaGraphvizFormatter_formatOperator(node);
114 }
115 else if (ASTNode_isFunction(node))
116 {
117 s = FormulaGraphvizFormatter_formatFunction(node);
118 }
119 else if (ASTNode_isInteger(node))
120 {
121 StringBuffer_appendInt(p, ASTNode_getInteger(node));
122 s = StringBuffer_toString(p);
123 }
124 else if (ASTNode_isRational(node))
125 {
126 s = FormulaGraphvizFormatter_formatRational(node);
127 }
128 else if (ASTNode_isReal(node))
129 {
130 s = FormulaGraphvizFormatter_formatReal(node);
131 }
132 else if ( !ASTNode_isUnknown(node) )
133 {
134 if (ASTNode_getName(node) == NULL)
135 {
136 StringBuffer_append(p, "unknown");
137 }
138 else
139 {
140 StringBuffer_append(p, ASTNode_getName(node));
141 }
142 s = StringBuffer_toString(p);
143 }
144
145 StringBuffer_free(p);
146
147 return s;
148 }
149
150
151 /**
152 * Since graphviz will interpret identical names as referring to
153 * the same node presentation-wise it is better if each function node
154 * has a unique name.
155 *
156 * Returns the name with the name of the first child
157 * prepended
158 *
159 * THIS COULD BE DONE BETTER
160 */
161 char *
FormulaGraphvizFormatter_getUniqueName(const ASTNode_t * node)162 FormulaGraphvizFormatter_getUniqueName (const ASTNode_t *node)
163 {
164 StringBuffer_t *p = StringBuffer_create(128);
165 char *s = NULL;
166
167 if (ASTNode_isOperator(node))
168 {
169 s = FormulaGraphvizFormatter_OperatorGetUniqueName(node);
170 }
171 else if (ASTNode_isFunction(node))
172 {
173 s = FormulaGraphvizFormatter_FunctionGetUniqueName(node);
174 }
175 else if (ASTNode_isInteger(node))
176 {
177 StringBuffer_appendInt(p, ASTNode_getInteger(node));
178 s = StringBuffer_toString(p);
179 }
180 else if (ASTNode_isRational(node))
181 {
182 s = FormulaGraphvizFormatter_formatRational(node);
183 }
184 else if (ASTNode_isReal(node))
185 {
186 s = FormulaGraphvizFormatter_formatReal(node);
187 }
188 else if ( !ASTNode_isUnknown(node) )
189 {
190 StringBuffer_append(p, ASTNode_getName(node));
191 s = StringBuffer_toString(p);
192 }
193
194 StringBuffer_free(p);
195
196 return s;
197 }
198
199 /**
200 * Formats the given ASTNode as a directed graph function name and returns the
201 * result as a string.
202 */
203 char *
FormulaGraphvizFormatter_formatFunction(const ASTNode_t * node)204 FormulaGraphvizFormatter_formatFunction (const ASTNode_t *node)
205 {
206 char *s;
207 StringBuffer_t *p = StringBuffer_create(128);
208 ASTNodeType_t type = ASTNode_getType(node);
209
210 switch (type)
211 {
212 case AST_FUNCTION_ARCCOS:
213 s = "acos";
214 break;
215
216 case AST_FUNCTION_ARCSIN:
217 s = "asin";
218 break;
219
220 case AST_FUNCTION_ARCTAN:
221 s = "atan";
222 break;
223
224 case AST_FUNCTION_CEILING:
225 s = "ceil";
226 break;
227
228 case AST_FUNCTION_LN:
229 s = "log";
230 break;
231
232 case AST_FUNCTION_POWER:
233 s = "pow";
234 break;
235
236 default:
237 if (ASTNode_getName(node) == NULL)
238 {
239 StringBuffer_append(p, "unknown");
240 }
241 else
242 {
243 StringBuffer_append(p, ASTNode_getName(node));
244 }
245 s = StringBuffer_toString(p);
246 break;
247 }
248
249 StringBuffer_free(p);
250
251 return s;
252 }
253
254
255 /**
256 * Since graphviz will interpret identical names as referring to
257 * the same node presentation-wise it is better if each function node
258 * has a unique name.
259 *
260 * Returns the name of the function with the name of the first child
261 * prepended
262 *
263 * THIS COULD BE DONE BETTER
264 */
265 char *
FormulaGraphvizFormatter_FunctionGetUniqueName(const ASTNode_t * node)266 FormulaGraphvizFormatter_FunctionGetUniqueName (const ASTNode_t *node)
267 {
268 char *s;
269 StringBuffer_t *p = StringBuffer_create(128);
270 ASTNodeType_t type = ASTNode_getType(node);
271
272 if (ASTNode_getNumChildren(node) != 0)
273 {
274 const char* name = ASTNode_getName(ASTNode_getChild(node,0));
275 if (name != NULL)
276 StringBuffer_append(p, name);
277 }
278 else
279 {
280 StringBuffer_append(p, "unknown");
281 }
282
283 switch (type)
284 {
285 case AST_FUNCTION_ARCCOS:
286 StringBuffer_append(p, "acos");
287 break;
288
289 case AST_FUNCTION_ARCSIN:
290 StringBuffer_append(p, "asin");
291 break;
292
293 case AST_FUNCTION_ARCTAN:
294 StringBuffer_append(p, "atan");
295 break;
296
297 case AST_FUNCTION_CEILING:
298 StringBuffer_append(p, "ceil");
299 break;
300
301 case AST_FUNCTION_LN:
302 StringBuffer_append(p, "log");
303 break;
304
305 case AST_FUNCTION_POWER:
306 StringBuffer_append(p, "pow");
307 break;
308
309 default:
310 if (ASTNode_getName(node) != NULL)
311 {
312 StringBuffer_append(p, ASTNode_getName(node));
313 }
314 break;
315 }
316
317 s = StringBuffer_toString(p);
318
319 StringBuffer_free(p);
320
321 return s;
322 }
323
324
325 /**
326 * Formats the given ASTNode as a directed graph operator and returns the result
327 * as a string.
328 */
329 char *
FormulaGraphvizFormatter_formatOperator(const ASTNode_t * node)330 FormulaGraphvizFormatter_formatOperator (const ASTNode_t *node)
331 {
332 char *s;
333 ASTNodeType_t type = ASTNode_getType(node);
334 StringBuffer_t *p = StringBuffer_create(128);
335
336 switch (type)
337 {
338 case AST_TIMES:
339 s = "times";
340 break;
341
342 case AST_DIVIDE:
343 s = "divide";
344 break;
345
346 case AST_PLUS:
347 s = "plus";
348 break;
349
350 case AST_MINUS:
351 s = "minus";
352 break;
353
354 case AST_POWER:
355 s = "power";
356 break;
357
358 default:
359 StringBuffer_appendChar(p, ASTNode_getCharacter(node));
360 s = StringBuffer_toString(p);
361 break;
362 }
363
364 StringBuffer_free(p);
365
366 return s;
367 }
368
369 /**
370 * Since graphviz will interpret identical names as referring to
371 * the same node presentation-wise it is better if each function node
372 * has a unique name.
373 *
374 * Returns the name of the operator with the name of the first child
375 * prepended
376 *
377 * THIS COULD BE DONE BETTER
378 */
379 char *
FormulaGraphvizFormatter_OperatorGetUniqueName(const ASTNode_t * node)380 FormulaGraphvizFormatter_OperatorGetUniqueName (const ASTNode_t *node)
381 {
382 char *s;
383 char number[10];
384 StringBuffer_t *p = StringBuffer_create(128);
385 ASTNodeType_t type = ASTNode_getType(node);
386
387 if (FormulaGraphvizFormatter_isFunction(ASTNode_getChild(node,0))
388 || ASTNode_isOperator(ASTNode_getChild(node,0)))
389 {
390 StringBuffer_append(p, "func");
391 }
392 else
393 {
394 if (ASTNode_isInteger(ASTNode_getChild(node, 0)))
395 {
396 sprintf(number, "%d", (int)ASTNode_getInteger(ASTNode_getChild(node, 0)));
397 StringBuffer_append(p, number);
398 }
399 else if (ASTNode_isReal(ASTNode_getChild(node, 0)))
400 {
401 sprintf(number, "%ld", ASTNode_getNumerator(ASTNode_getChild(node, 0)));
402 StringBuffer_append(p, number);
403 }
404 else
405 {
406 StringBuffer_append(p, ASTNode_getName(ASTNode_getChild(node,0)));
407 }
408 }
409
410 switch (type)
411 {
412 case AST_TIMES:
413 StringBuffer_append(p, "times");
414 break;
415
416 case AST_DIVIDE:
417 StringBuffer_append(p, "divide");
418 break;
419
420 case AST_PLUS:
421 StringBuffer_append(p, "plus");
422 break;
423
424 case AST_MINUS:
425 StringBuffer_append(p, "minus");
426 break;
427
428 case AST_POWER:
429 StringBuffer_append(p, "power");
430 break;
431
432 default:
433 StringBuffer_appendChar(p, ASTNode_getCharacter(node));
434 break;
435 }
436
437 s = StringBuffer_toString(p);
438
439 StringBuffer_free(p);
440
441 return s;
442 }
443
444
445 /**
446 * Formats the given ASTNode as a rational number and returns the result as
447 * a string. This amounts to:
448 *
449 * "(numerator/denominator)"
450 */
451 char *
FormulaGraphvizFormatter_formatRational(const ASTNode_t * node)452 FormulaGraphvizFormatter_formatRational (const ASTNode_t *node)
453 {
454 char *s;
455 StringBuffer_t *p = StringBuffer_create(128);
456
457 StringBuffer_appendChar( p, '(');
458 StringBuffer_appendInt ( p, ASTNode_getNumerator(node) );
459 StringBuffer_appendChar( p, '/');
460 StringBuffer_appendInt ( p, ASTNode_getDenominator(node) );
461 StringBuffer_appendChar( p, ')');
462
463 s = StringBuffer_toString(p);
464
465 StringBuffer_free(p);
466
467 return s;
468 }
469
470
471 /**
472 * Formats the given ASTNode as a real number and returns the result as
473 * a string.
474 */
475 char *
FormulaGraphvizFormatter_formatReal(const ASTNode_t * node)476 FormulaGraphvizFormatter_formatReal (const ASTNode_t *node)
477 {
478 StringBuffer_t *p = StringBuffer_create(128);
479 double value = ASTNode_getReal(node);
480 int sign;
481 char *s;
482
483 if (util_isNaN(value))
484 {
485 s = "NaN";
486 }
487 else if ((sign = util_isInf(value)) != 0)
488 {
489 if (sign == -1)
490 {
491 s = "-INF";
492 }
493 else
494 {
495 s = "INF";
496 }
497 }
498 else if (util_isNegZero(value))
499 {
500 s = "-0";
501 }
502 else
503 {
504 StringBuffer_appendReal(p, value);
505 s = StringBuffer_toString(p);
506 }
507
508 StringBuffer_free(p);
509
510 return s;
511 }
512
513
514 /**
515 * Visits the given ASTNode node. This function is really just a
516 * dispatcher to either FormulaGraphvizFormatter_visitFunction() or
517 * FormulaGraphvizFormatter_visitOther().
518 */
519 void
FormulaGraphvizFormatter_visit(const ASTNode_t * parent,const ASTNode_t * node,StringBuffer_t * sb)520 FormulaGraphvizFormatter_visit (const ASTNode_t *parent,
521 const ASTNode_t *node,
522 StringBuffer_t *sb )
523 {
524 if (ASTNode_isLog10(node))
525 {
526 FormulaGraphvizFormatter_visitLog10(parent, node, sb);
527 }
528 else if (ASTNode_isSqrt(node))
529 {
530 FormulaGraphvizFormatter_visitSqrt(parent, node, sb);
531 }
532 else if (FormulaGraphvizFormatter_isFunction(node))
533 {
534 FormulaGraphvizFormatter_visitFunction(parent, node, sb);
535 }
536 else if (ASTNode_isUMinus(node))
537 {
538 FormulaGraphvizFormatter_visitUMinus(parent, node, sb);
539 }
540 else
541 {
542 FormulaGraphvizFormatter_visitOther(parent, node, sb);
543 }
544 }
545
546
547 /**
548 * Visits the given ASTNode as a function. For this node only the
549 * traversal is preorder.
550 * Writes the function as a directed graph and appends the result
551 * to the StringBuffer.
552 */
553 void
FormulaGraphvizFormatter_visitFunction(const ASTNode_t * parent,const ASTNode_t * node,StringBuffer_t * sb)554 FormulaGraphvizFormatter_visitFunction (const ASTNode_t *parent,
555 const ASTNode_t *node,
556 StringBuffer_t *sb )
557 {
558 unsigned int numChildren = ASTNode_getNumChildren(node);
559 unsigned int n;
560 char *name;
561 char *uniqueName;
562
563 uniqueName = FormulaGraphvizFormatter_getUniqueName(node);
564 name = FormulaGraphvizFormatter_format(node);
565
566 StringBuffer_append(sb, uniqueName);
567 StringBuffer_append(sb, " [shape=box, label=");
568 StringBuffer_append(sb, name);
569 StringBuffer_append(sb, "];\n");
570
571 if (parent != NULL)
572 {
573 name = FormulaGraphvizFormatter_getUniqueName(node);
574 uniqueName = FormulaGraphvizFormatter_getUniqueName(parent);
575
576 if(strcmp(name, uniqueName))
577 {
578 StringBuffer_append(sb, uniqueName);
579 StringBuffer_append(sb, " -> ");
580 StringBuffer_append(sb, name);
581 StringBuffer_append(sb, ";\n");
582 }
583 free(name);
584 free(uniqueName);
585 }
586
587 if (numChildren > 0)
588 {
589 FormulaGraphvizFormatter_visit( node, ASTNode_getChild(node, 0), sb );
590 }
591
592 for (n = 1; n < numChildren; n++)
593 {
594 FormulaGraphvizFormatter_visit( node, ASTNode_getChild(node, n), sb );
595 }
596
597 }
598
599
600 /**
601 * Visits the given ASTNode as the function "log(10, x)" and in doing so,
602 * formats it as "log10(x)" (where x is any subexpression).
603 * Writes the function as a directed graph and appends the result
604 * to the StringBuffer.
605 *
606 * A seperate function may not be strictly speaking necessary for graphs
607 */
608 void
FormulaGraphvizFormatter_visitLog10(const ASTNode_t * parent,const ASTNode_t * node,StringBuffer_t * sb)609 FormulaGraphvizFormatter_visitLog10 (const ASTNode_t *parent,
610 const ASTNode_t *node,
611 StringBuffer_t *sb )
612 {
613 char *uniqueName = FormulaGraphvizFormatter_getUniqueName(node);
614 char *name = FormulaGraphvizFormatter_format(node);
615
616 StringBuffer_append(sb, uniqueName);
617 StringBuffer_append(sb, " [shape=box, label=");
618 StringBuffer_append(sb, name);
619 StringBuffer_append(sb, "];\n");
620
621 FormulaGraphvizFormatter_visit(node, ASTNode_getChild(node, 1), sb);
622 }
623
624
625 /**
626 * Visits the given ASTNode as the function "root(2, x)" and in doing so,
627 * formats it as "sqrt(x)" (where x is any subexpression).
628 * Writes the function as a directed graph and appends the result
629 * to the StringBuffer.
630 *
631 * A seperate function may not be strictly speaking necessary for graphs
632 */
633 void
FormulaGraphvizFormatter_visitSqrt(const ASTNode_t * parent,const ASTNode_t * node,StringBuffer_t * sb)634 FormulaGraphvizFormatter_visitSqrt (const ASTNode_t *parent,
635 const ASTNode_t *node,
636 StringBuffer_t *sb )
637 {
638 char *uniqueName = FormulaGraphvizFormatter_getUniqueName(node);
639 char *name = FormulaGraphvizFormatter_format(node);
640
641 StringBuffer_append(sb, uniqueName);
642 StringBuffer_append(sb, " [shape=box, label=");
643 StringBuffer_append(sb, name);
644 StringBuffer_append(sb, "];\n");
645
646 FormulaGraphvizFormatter_visit(node, ASTNode_getChild(node, 1), sb);
647 }
648
649
650 /**
651 * Visits the given ASTNode as a unary minus. For this node only the
652 * traversal is preorder.
653 * Writes the function as a directed graph and appends the result
654 * to the StringBuffer.
655 */
656 void
FormulaGraphvizFormatter_visitUMinus(const ASTNode_t * parent,const ASTNode_t * node,StringBuffer_t * sb)657 FormulaGraphvizFormatter_visitUMinus (const ASTNode_t *parent,
658 const ASTNode_t *node,
659 StringBuffer_t *sb )
660 {
661 char *uniqueName = FormulaGraphvizFormatter_getUniqueName(node);
662 char *name = FormulaGraphvizFormatter_format(node);
663
664 StringBuffer_append(sb, uniqueName);
665 StringBuffer_append(sb, " [shape=box, label=");
666 StringBuffer_append(sb, name);
667 StringBuffer_append(sb, "];\n");
668
669 if (parent != NULL)
670 {
671 uniqueName = FormulaGraphvizFormatter_getUniqueName(parent);
672 name = FormulaGraphvizFormatter_getUniqueName(node);
673
674 if(strcmp(name, uniqueName))
675 {
676 StringBuffer_append(sb, uniqueName);
677 StringBuffer_append(sb, " -> ");
678 StringBuffer_append(sb, name);
679 StringBuffer_append(sb, ";\n");
680 }
681 }
682
683 FormulaGraphvizFormatter_visit ( node, ASTNode_getLeftChild(node), sb );
684 }
685
686
687 /**
688 * Visits the given ASTNode and continues the inorder traversal.
689 * Writes the function as a directed graph and appends the result
690 * to the StringBuffer.
691 */
692 void
FormulaGraphvizFormatter_visitOther(const ASTNode_t * parent,const ASTNode_t * node,StringBuffer_t * sb)693 FormulaGraphvizFormatter_visitOther (const ASTNode_t *parent,
694 const ASTNode_t *node,
695 StringBuffer_t *sb )
696 {
697 unsigned int numChildren = ASTNode_getNumChildren(node);
698 char *name;
699 char *uniqueName;
700
701 if (numChildren > 0)
702 {
703 uniqueName = FormulaGraphvizFormatter_getUniqueName(node);
704 name = FormulaGraphvizFormatter_format(node);
705
706 StringBuffer_append(sb, uniqueName);
707 StringBuffer_append(sb, " [shape=box, label=");
708 StringBuffer_append(sb, name);
709 StringBuffer_append(sb, "];\n");
710 free(uniqueName);
711
712 FormulaGraphvizFormatter_visit( node, ASTNode_getLeftChild(node), sb );
713 }
714
715 if (parent != NULL)
716 {
717 name = FormulaGraphvizFormatter_getUniqueName(node);
718 uniqueName = FormulaGraphvizFormatter_getUniqueName(parent);
719
720 if(strcmp(name, uniqueName))
721 {
722 StringBuffer_append(sb, uniqueName);
723 StringBuffer_append(sb, " -> ");
724 StringBuffer_append(sb, name);
725 StringBuffer_append(sb, ";\n");
726 }
727 free(name);
728 free(uniqueName);
729 }
730
731 if (numChildren > 1)
732 {
733 FormulaGraphvizFormatter_visit( node, ASTNode_getRightChild(node), sb );
734 }
735 }
736
737
738 void
printFunctionDefinition(unsigned int n,FunctionDefinition_t * fd)739 printFunctionDefinition (unsigned int n, FunctionDefinition_t *fd)
740 {
741 const ASTNode_t *math;
742 char *formula;
743
744
745 if ( FunctionDefinition_isSetMath(fd) )
746 {
747 math = FunctionDefinition_getMath(fd);
748
749 /* Print function body. */
750 if (ASTNode_getNumChildren(math) == 0)
751 {
752 printf("(no body defined)");
753 }
754 else
755 {
756 math = ASTNode_getChild(math, ASTNode_getNumChildren(math) - 1);
757 formula = SBML_formulaToDot(math);
758 fprintf(fout, "subgraph cluster%u {\n", noClusters);
759 fprintf(fout, "label=\"FunctionDefinition: %s\";\n%s\n", FunctionDefinition_getId(fd), formula);
760 free(formula);
761 noClusters++;
762 }
763 }
764 }
765
766
767 void
printRuleMath(unsigned int n,Rule_t * r)768 printRuleMath (unsigned int n, Rule_t *r)
769 {
770 char *formula;
771
772 if ( Rule_isSetMath(r) )
773 {
774 formula = SBML_formulaToDot( Rule_getMath(r));
775 fprintf(fout, "subgraph cluster%u {\n", noClusters);
776 fprintf(fout, "label=\"Rule: %u\";\n%s\n", n, formula);
777 free(formula);
778 noClusters++;
779 }
780 }
781
782
783 void
printReactionMath(unsigned int n,Reaction_t * r)784 printReactionMath (unsigned int n, Reaction_t *r)
785 {
786 char *formula;
787 KineticLaw_t *kl;
788
789
790 if (Reaction_isSetKineticLaw(r))
791 {
792 kl = Reaction_getKineticLaw(r);
793
794 if ( KineticLaw_isSetMath(kl) )
795 {
796 formula = SBML_formulaToDot( KineticLaw_getMath(kl) );
797 fprintf(fout, "subgraph cluster%u {\n", noClusters);
798 fprintf(fout, "label=\"Reaction: %s\";\n%s\n", Reaction_getId(r), formula);
799 free(formula);
800 noClusters++;
801 }
802 }
803 }
804
805
806 void
printEventAssignmentMath(unsigned int n,EventAssignment_t * ea)807 printEventAssignmentMath (unsigned int n, EventAssignment_t *ea)
808 {
809 const char *variable;
810 char *formula;
811
812
813 if ( EventAssignment_isSetMath(ea) )
814 {
815 variable = EventAssignment_getVariable(ea);
816 formula = SBML_formulaToDot( EventAssignment_getMath(ea) );
817 fprintf(fout, "subgraph cluster%u {\n", noClusters);
818 fprintf(fout, "label=\"EventAssignment: %u\";\n", n);
819 fprintf(fout, "%s [shape=box];\n%s -> %s\n", variable, variable, formula);
820 noClusters++;
821 free(formula);
822 }
823 }
824
825
826 void
printEventMath(unsigned int n,Event_t * e)827 printEventMath (unsigned int n, Event_t *e)
828 {
829 char *formula;
830 unsigned int i;
831
832
833 if ( Event_isSetDelay(e) )
834 {
835 formula = SBML_formulaToDot( Delay_getMath(Event_getDelay(e)) );
836 fprintf(fout, "subgraph cluster%u {\n", noClusters);
837 fprintf(fout, "label=\"Event %s delay:\";\n%s\n", Event_getId(e), formula);
838 free(formula);
839 noClusters++;
840 }
841
842 if ( Event_isSetTrigger(e) )
843 {
844 formula = SBML_formulaToDot( Trigger_getMath(Event_getTrigger(e)) );
845 fprintf(fout, "subgraph cluster%u {\n", noClusters);
846 fprintf(fout, "label=\"Event %s trigger:\";\n%s\n", Event_getId(e), formula);
847 noClusters++;
848 free(formula);
849 }
850
851 for (i = 0; i < Event_getNumEventAssignments(e); ++i)
852 {
853 printEventAssignmentMath(i + 1, Event_getEventAssignment(e, i));
854 }
855 }
856
857
858 void
printMath(Model_t * m)859 printMath (Model_t *m)
860 {
861 unsigned int n;
862
863 /* a digraph must have a name thus
864 * need to check that Model_getId does not return NULL
865 * and provide a name if it does
866 */
867
868 if (Model_getId(m) != NULL) {
869 fprintf(fout, "digraph %s {\n", Model_getId(m));
870 }
871 else {
872 fprintf(fout, "digraph example {\n");
873 }
874 fprintf(fout, "compound=true;\n");
875
876 for (n = 0; n < Model_getNumFunctionDefinitions(m); ++n)
877 {
878 printFunctionDefinition(n + 1, Model_getFunctionDefinition(m, n));
879 }
880
881 for (n = 0; n < Model_getNumRules(m); ++n)
882 {
883 printRuleMath(n + 1, Model_getRule(m, n));
884 }
885
886 printf("\n");
887
888 for (n = 0; n < Model_getNumReactions(m); ++n)
889 {
890 printReactionMath(n + 1, Model_getReaction(m, n));
891 }
892
893 printf("\n");
894
895 for (n = 0; n < Model_getNumEvents(m); ++n)
896 {
897 printEventMath(n + 1, Model_getEvent(m, n));
898 }
899
900 fprintf(fout, "}\n");
901 }
902
903
904 int
main(int argc,char * argv[])905 main (int argc, char *argv[])
906 {
907 SBMLDocument_t *d;
908 Model_t *m;
909
910 if (argc != 3)
911 {
912 printf("\n usage: drawMath <sbml filename> <output dot filename>\n\n");
913 return 1;
914 }
915
916 d = readSBML(argv[1]);
917 m = SBMLDocument_getModel(d);
918
919 SBMLDocument_printErrors(d, stdout);
920
921 if ((fout = fopen( argv[2], "w" )) == NULL )
922 {
923 printf( "The output file was not opened\n" );
924 }
925 else
926 {
927 printMath(m);
928 fclose(fout);
929 }
930
931 SBMLDocument_free(d);
932
933 return 0;
934 }
935