+++ /dev/null
-/************************************************************************
- ************************************************************************
- FAUST compiler
- Copyright (C) 2003-2004 GRAME, Centre National de Creation Musicale
- ---------------------------------------------------------------------
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- ************************************************************************
- ************************************************************************/
-
- /**
- * @file drawschema.cpp
- * Implement block-diagram schema generation in svg or postscript format.
- * The result is a folder containing one or more schema files in svg or
- * ps format. Complex block-diagrams are automatically splitted.
- */
-
-
-#include <stdio.h>
-#include <ctype.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <errno.h>
-#include <string.h>
-
-#include <ostream>
-#include <sstream>
-#include <set>
-#include <utility>
-#include <map>
-#include <stack>
-#include <string>
-
-#include "boxes.hh"
-#include "ppbox.hh"
-#include "prim2.hh"
-
-#include <vector>
-#include "devLib.h"
-#include "ppbox.hh"
-#include "xtended.hh"
-#include "occurrences.hh"
-#include "boxcomplexity.h"
-
-#include "schema.h"
-#include "drawschema.hh"
-#include "compatibility.hh"
-#include "names.hh"
-#include "description.hh"
-#include "property.hh"
-
-
-
-#if 0
-#define linkcolor "#b3d1dc"
-#define normalcolor "#ffeaa2"
-#define uicolor "#F1CFA1"
-#define slotcolor "#ffffd7"
-#define numcolor "#ffffff"
-#endif
-
-#if 0
-#define linkcolor "#F57900"
-#define normalcolor "#4B71A1"
-#define uicolor "#47945E"
-#define slotcolor "#EDD400"
-#define numcolor "#4B71A1"
-#endif
-
-#if 0
-#define linkcolor "#47945E"
-#define normalcolor "#4B71A1"
-#define uicolor "#f44800"
-#define slotcolor "#EDD400"
-#define numcolor "#f44800"
-#endif
-
-#if 0
-#define linkcolor "#47945E"
-#define normalcolor "#4B71A1"
-#define uicolor "#816647"
-#define slotcolor "#EDD400"
-#define numcolor "#f44800"
-#endif
-
-#if 0
-#define linkcolor "#003366"
-#define normalcolor "#4B71A1"
-#define uicolor "#816647"
-#define slotcolor "#EDD400"
-#define numcolor "#f44800"
-#endif
-
-#if 0
-#define linkcolor "#003366"
-#define normalcolor "#4B71A1"
-#define uicolor "#477881"
-#define slotcolor "#816647"
-#define numcolor "#f44800"
-#endif
-
-
-#if 1
-#define linkcolor "#003366"
-#define normalcolor "#4B71A1"
-#define uicolor "#477881"
-#define slotcolor "#47945E"
-#define numcolor "#f44800"
-#define invcolor "#ffffff"
-#endif
-
-using namespace std;
-
-// external parameters
-extern int gFoldThreshold; // max diagram complexity before folding
-
-
-// internal state during drawing
-static Occurrences* gOccurrences;
-static bool sFoldingFlag; // true with complex block-diagrams
-static stack<Tree> gPendingExp; // Expressions that need to be drawn
-static set<Tree> gDrawnExp; // Expressions drawn or scheduled so far
-static const char* gDevSuffix; // .svg or .ps used to choose output device
-static char gCurrentDir[512]; // room to save current directory name
-static string gSchemaFileName; // name of schema file beeing generated
-static map<Tree,string> gBackLink; // link to enclosing file for sub schema
-
-// prototypes of internal functions
-static void writeSchemaFile(Tree bd);
-static schema* generateDiagramSchema (Tree bd);
-static schema* generateInsideSchema(Tree t);
-static void scheduleDrawing(Tree t);
-static bool pendingDrawing(Tree& t);
-static schema* generateAbstractionSchema(schema* x, Tree t);
-static schema* generateOutputSlotSchema(Tree a);
-static schema* generateInputSlotSchema(Tree a);
-static schema* generateBargraphSchema(Tree t);
-static schema* generateUserInterfaceSchema(Tree t);
-static char* legalFileName(Tree t, int n, char* dst);
-static int cholddir ();
-static int mkchdir(const char* dirname);
-
-
-
-
-/**
- *The entry point to generate from a block diagram as a set of
- *svg files stored in the directory "<projname>-svg/" or
- *"<projname>-ps/" depending of <dev>.
- */
-void drawSchema(Tree bd, const char* projname, const char* dev)
-{
- gDevSuffix = dev;
- sFoldingFlag = boxComplexity(bd) > gFoldThreshold;
-
- mkchdir(projname); // create a directory to store files
-
- scheduleDrawing(bd); // schedule the initial drawing
-
- Tree t; while (pendingDrawing(t)) {
- writeSchemaFile(t); // generate all the pending drawing
- }
-
- cholddir(); // return to current directory
-}
-
-
-/************************************************************************
- ************************************************************************
- IMPLEMENTATION
- ************************************************************************
- ************************************************************************/
-
-
-//------------------- to schedule and retreive drawing ------------------
-
-/**
- * Schedule a makeBlockSchema diagram to be drawn.
- */
-static void scheduleDrawing(Tree t)
-{
- if (gDrawnExp.find(t) == gDrawnExp.end()) {
- gDrawnExp.insert(t);
- gBackLink.insert(make_pair(t,gSchemaFileName)); // remember the enclosing filename
- gPendingExp.push(t);
- }
-}
-
-/**
- * Retrieve next block diagram that must be drawn
- */
-static bool pendingDrawing(Tree& t)
-{
- if (gPendingExp.empty()) return false;
- t = gPendingExp.top();
- gPendingExp.pop();
- return true;
-}
-
-
-
-//------------------------ dealing with files -------------------------
-
-/**
- * Write a top level diagram. A top level diagram
- * is decorated with its definition name property
- * and is drawn in an individual file
- */
-static void writeSchemaFile(Tree bd)
-{
- Tree id;
- schema* ts;
-
- char temp[1024];
-
- gOccurrences = new Occurrences(bd);
-
- bool hasname = getDefNameProperty(bd, id);
-
- //assert(hasname);
- if (!hasname) {
- // create an arbitrary name
- id = tree(Node(unique("diagram_")));
- }
-
- // generate legal file name for the schema
- stringstream s1; s1 << legalFileName(bd, 1024, temp) << "." << gDevSuffix;
- gSchemaFileName = s1.str();
-
- // generate the label of the schema
- stringstream s2; s2 << tree2str(id);
- string link = gBackLink[bd];
- ts = makeTopSchema(generateInsideSchema(bd), 20, s2.str(), link);
- // draw to the device defined by gDevSuffix
- if (strcmp(gDevSuffix, "svg") == 0) {
- SVGDev dev(s1.str().c_str(), ts->width(), ts->height());
- ts->place(0,0, kLeftRight);
- ts->draw(dev);
- { collector c; ts->collectTraits(c); c.draw(dev); }
- } else {
- PSDev dev(s1.str().c_str(), ts->width(), ts->height());
- ts->place(0,0, kLeftRight);
- ts->draw(dev);
- {
- collector c;
- ts->collectTraits(c);
- c.draw(dev);
- }
- }
-}
-
-
-
-/**
- * Create a new directory in the current one to store the diagrams.
- * The current directory is saved to be later restaured.
- */
-static int mkchdir(const char* dirname)
-{
- //cerr << "mkchdir of " << dirname << endl;
- if (getcwd(gCurrentDir, 512) != 0) {
- int status = mkdir(dirname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
- if (status == 0 || errno == EEXIST) {
- if (chdir(dirname) == 0) {
- return 0;
- }
- }
- }
- perror("mkchdir");
- exit(errno);
- //return errno;
-}
-
-
-/**
- *Switch back to the previously stored current directory
- */
-static int cholddir ()
-{
- if (chdir(gCurrentDir) == 0) {
- return 0;
- } else {
- perror("cholddir");
- exit(errno);
- }
-}
-
-
-/**
- * Transform the definition name property of tree <t> into a
- * legal file name. The resulting file name is stored in
- * <dst> a table of at least <n> chars. Returns the <dst> pointer
- * for convenience.
- */
-static char* legalFileName(Tree t, int n, char* dst)
-{
- Tree id;
- int i=0;
- if (getDefNameProperty(t, id)) {
- const char* src = tree2str(id);
- for (i=0; isalnum(src[i]) && i<16; i++) {
- dst[i] = src[i];
- }
- }
- dst[i] = 0;
- if (strcmp(dst, "process") != 0) {
- // if it is not process add the hex address to make the name unique
- snprintf(&dst[i], n-i, "-%p", t);
- }
- return dst;
-}
-
-
-
-//------------------------ generating the schema -------------------------
-
-
-/**
- * isInverter(t) returns true if t == '*(-1)'. This test is used
- * to simplify diagram by using a special symbol for inverters.
- */
-Tree gInverter[6];
-
-static bool isInverter(Tree t)
-{
- // init gInverted table. For some reason doesn't work if done outside
- if (gInverter[0] == 0) {
- gInverter[0] = boxSeq(boxPar(boxWire(), boxInt(-1)),boxPrim2(sigMul));
- gInverter[1] = boxSeq(boxPar(boxInt(-1), boxWire()),boxPrim2(sigMul));
- gInverter[2] = boxSeq(boxPar(boxWire(), boxReal(-1.0)),boxPrim2(sigMul));
- gInverter[3] = boxSeq(boxPar(boxReal(-1.0), boxWire()),boxPrim2(sigMul));
- gInverter[4] = boxSeq(boxPar(boxInt(0), boxWire()),boxPrim2(sigSub));
- gInverter[5] = boxSeq(boxPar(boxReal(0.0), boxWire()),boxPrim2(sigSub));
- };
-
- //cerr << "isInverter " << t << '$' << boxpp(t) << endl;
- for (int i=0; i<6; i++) {
- if (t == gInverter[i]) return true;
- }
- return false;
-}
-
-
-/**
- * Compute the Pure Routing property, that is expressions
- * only made of cut, wires and slots. No labels will be
- * dispayed for pure routing expressions.
- */
-property<bool> gPureRoutingProperty;
-
-static bool isPureRouting(Tree t)
-{
- bool r;
- int ID;
- Tree x,y;
-
- if (gPureRoutingProperty.get(t,r)) {
- return r;
- } else if ( isBoxCut(t)
- || isBoxWire(t)
- || isInverter(t)
- || isBoxSlot(t, &ID)
- || (isBoxPar(t,x,y) && isPureRouting(x) && isPureRouting(y))
- || (isBoxSeq(t,x,y) && isPureRouting(x) && isPureRouting(y))
- || (isBoxSplit(t,x,y) && isPureRouting(x) && isPureRouting(y))
- || (isBoxMerge(t,x,y) && isPureRouting(x) && isPureRouting(y))
- ) {
- gPureRoutingProperty.set(t,true);
- return true;
- } else {
- gPureRoutingProperty.set(t,false);
- return false;
- }
-}
-
-
-/**
- * Generate an appropriate schema according to
- * the type of block diagram. When folding is requiered,
- * instead of going down block-diagrams with a name,
- * schedule them for an individual file.
- */
-static schema* generateDiagramSchema(Tree t)
-{
- Tree id;
- int ins, outs;
-
- //cerr << t << " generateDiagramSchema " << boxpp(t)<< endl;
-
- if (getDefNameProperty(t, id)) {
- stringstream s; s << tree2str(id);
- //cerr << t << "\tNAMED : " << s.str() << endl;
- }
-
- if ( sFoldingFlag && /*(gOccurrences->getCount(t) > 0) &&*/
- (boxComplexity(t) > 2) && getDefNameProperty(t, id)) {
- char temp[1024];
- getBoxType(t, &ins, &outs);
- stringstream s, l;
- s << tree2str(id);
- l << legalFileName(t,1024,temp) << "." << gDevSuffix;
- scheduleDrawing(t);
- return makeBlockSchema(ins, outs, s.str(), linkcolor, l.str());
-
- } else if (getDefNameProperty(t, id) && ! isPureRouting(t)) {
- // named case : not a slot, with a name
- // draw a line around the object with its name
- stringstream s; s << tree2str(id);
- return makeDecorateSchema(generateInsideSchema(t), 10, s.str());
-
- } else {
- // normal case
- return generateInsideSchema(t);
- }
-}
-
-
-
-/**
- * Generate the inside schema of a block diagram
- * according to its type
- */
-static schema* generateInsideSchema(Tree t)
-{
- Tree a, b, ff, l, type,name,file;
- int i;
- double r;
- prim0 p0;
- prim1 p1;
- prim2 p2;
- prim3 p3;
- prim4 p4;
- prim5 p5;
-
-
- xtended* xt = (xtended*)getUserData(t);
-
- if (xt) { return makeBlockSchema(xt->arity(), 1, xt->name(), normalcolor, ""); }
-
- else if (isInverter(t)) { return makeInverterSchema(invcolor); }
-
- else if (isBoxInt(t, &i)) { stringstream s; s << i; return makeBlockSchema(0, 1, s.str(), numcolor, "" ); }
- else if (isBoxReal(t, &r)) { stringstream s; s << r; return makeBlockSchema(0, 1, s.str(), numcolor, "" ); }
- else if (isBoxWire(t)) { return makeCableSchema(); }
- else if (isBoxCut(t)) { return makeCutSchema(); }
-
- else if (isBoxPrim0(t, &p0)) { return makeBlockSchema(0, 1, prim0name(p0), normalcolor, ""); }
- else if (isBoxPrim1(t, &p1)) { return makeBlockSchema(1, 1, prim1name(p1), normalcolor, ""); }
- else if (isBoxPrim2(t, &p2)) { return makeBlockSchema(2, 1, prim2name(p2), normalcolor, ""); }
- else if (isBoxPrim3(t, &p3)) { return makeBlockSchema(3, 1, prim3name(p3), normalcolor, ""); }
- else if (isBoxPrim4(t, &p4)) { return makeBlockSchema(4, 1, prim4name(p4), normalcolor, ""); }
- else if (isBoxPrim5(t, &p5)) { return makeBlockSchema(5, 1, prim5name(p5), normalcolor, ""); }
-
- else if (isBoxFFun(t, ff)) { return makeBlockSchema(ffarity(ff), 1, ffname(ff), normalcolor, ""); }
- else if (isBoxFConst(t, type,name,file)) { return makeBlockSchema(0, 1, tree2str(name), normalcolor, ""); }
- else if (isBoxFVar (t, type, name,file)) { return makeBlockSchema(0, 1, tree2str(name), normalcolor, ""); }
-
- else if (isBoxButton(t)) { return generateUserInterfaceSchema(t); }
- else if (isBoxCheckbox(t)) { return generateUserInterfaceSchema(t); }
- else if (isBoxVSlider(t)) { return generateUserInterfaceSchema(t); }
- else if (isBoxHSlider(t)) { return generateUserInterfaceSchema(t); }
- else if (isBoxNumEntry(t)) { return generateUserInterfaceSchema(t); }
- else if (isBoxVBargraph(t)) { return generateBargraphSchema(t); }
- else if (isBoxHBargraph(t)) { return generateBargraphSchema(t); }
-
- // don't draw group rectangle when labels are empty (ie "")
- else if (isBoxVGroup(t,l,a)) { stringstream s; s << "vgroup(" << extractName(l) << ")";
- schema* r = generateDiagramSchema(a);
- return makeDecorateSchema(r, 10, s.str()); }
- else if (isBoxHGroup(t,l,a)) { stringstream s; s << "hgroup(" << extractName(l) << ")";
- schema* r = generateDiagramSchema(a);
- return makeDecorateSchema(r, 10, s.str()); }
- else if (isBoxTGroup(t,l,a)) { stringstream s; s << "tgroup(" << extractName(l) << ")";
- schema* r = generateDiagramSchema(a);
- return makeDecorateSchema(r, 10, s.str()); }
-
- else if (isBoxSeq(t, a, b)) { return makeSeqSchema(generateDiagramSchema(a), generateDiagramSchema(b)); }
- else if (isBoxPar(t, a, b)) { return makeParSchema(generateDiagramSchema(a), generateDiagramSchema(b)); }
- else if (isBoxSplit(t, a, b)) { return makeSplitSchema(generateDiagramSchema(a), generateDiagramSchema(b)); }
- else if (isBoxMerge(t, a, b)) { return makeMergeSchema(generateDiagramSchema(a), generateDiagramSchema(b)); }
- else if (isBoxRec(t, a, b)) { return makeRecSchema(generateDiagramSchema(a), generateDiagramSchema(b)); }
-
- else if (isBoxSlot(t, &i)) { return generateOutputSlotSchema(t); }
- else if (isBoxSymbolic(t,a,b)) {
- Tree id;
- if (getDefNameProperty(t, id)) {
- return generateAbstractionSchema(generateInputSlotSchema(a), b);
- } else {
- return makeDecorateSchema(generateAbstractionSchema(generateInputSlotSchema(a), b), 10, "Abstraction");
- }
- }
-
- else {
-
- fprintf(stderr, "Internal Error, box expression not recognized : "); print(t, stderr); fprintf(stderr, "\n");
- exit(1);
-
- }
-}
-
-/**
- * Convert User interface element into a textual representation
- */
-static void UserInterfaceDescription(Tree box, string& d)
-{
- Tree t1, label, cur, min, max, step;
- stringstream fout;
- // user interface
- if (isBoxButton(box, label)) fout << "button(" << extractName(label) << ')';
- else if (isBoxCheckbox(box, label)) fout << "checkbox(" << extractName(label) << ')';
- else if (isBoxVSlider(box, label, cur, min, max, step)) {
- fout << "vslider("
- << extractName(label) << ", "
- << boxpp(cur) << ", "
- << boxpp(min) << ", "
- << boxpp(max) << ", "
- << boxpp(step)<< ')';
- }
- else if (isBoxHSlider(box, label, cur, min, max, step)) {
- fout << "hslider("
- << extractName(label) << ", "
- << boxpp(cur) << ", "
- << boxpp(min) << ", "
- << boxpp(max) << ", "
- << boxpp(step)<< ')';
- }
- else if (isBoxVGroup(box, label, t1)) {
- fout << "vgroup(" << extractName(label) << ", " << boxpp(t1, 0) << ')';
- }
- else if (isBoxHGroup(box, label, t1)) {
- fout << "hgroup(" << extractName(label) << ", " << boxpp(t1, 0) << ')';
- }
- else if (isBoxTGroup(box, label, t1)) {
- fout << "tgroup(" << extractName(label) << ", " << boxpp(t1, 0) << ')';
- }
- else if (isBoxHBargraph(box, label, min, max)) {
- fout << "hbargraph("
- << extractName(label) << ", "
- << boxpp(min) << ", "
- << boxpp(max) << ')';
- }
- else if (isBoxVBargraph(box, label, min, max)) {
- fout << "vbargraph("
- << extractName(label) << ", "
- << boxpp(min) << ", "
- << boxpp(max) << ')';
- }
- else if (isBoxNumEntry(box, label, cur, min, max, step)) {
- fout << "nentry("
- << extractName(label) << ", "
- << boxpp(cur) << ", "
- << boxpp(min) << ", "
- << boxpp(max) << ", "
- << boxpp(step)<< ')';
- }
- else {
- cerr << "INTERNAL ERROR : unknow user interface element " << endl;
- exit(0);
- }
- d = fout.str();
-}
-
-
-/**
- * Generate a 0->1 block schema for a user interface element
- */
-static schema* generateUserInterfaceSchema(Tree t)
-{
- string s; UserInterfaceDescription(t,s);
- return makeBlockSchema(0, 1, s, uicolor, "");
-}
-
-
-/**
- * Generate a 1->1 block schema for a user interface bargraph
- */
-static schema* generateBargraphSchema(Tree t)
-{
- string s; UserInterfaceDescription(t,s);
- return makeBlockSchema(1, 1, s, uicolor, "");
-}
-
-
-
-/**
- * Generate a 1->0 block schema for an input slot
- */
-static schema* generateInputSlotSchema(Tree a)
-{
- Tree id; assert(getDefNameProperty(a, id));
- stringstream s; s << tree2str(id);
- return makeBlockSchema(1, 0, s.str(), slotcolor, "");
-}
-
-
-
-/**
- * Generate a 0->1 block schema for an output slot
- */
-static schema* generateOutputSlotSchema(Tree a)
-{
- Tree id; assert(getDefNameProperty(a, id));
- stringstream s; s << tree2str(id);
- return makeBlockSchema(0, 1, s.str(), slotcolor, "");
-}
-
-
-
-/**
- * Generate an abstraction schema by placing in sequence
- * the input slots and the body
- */
-static schema* generateAbstractionSchema(schema* x, Tree t)
-{
- Tree a,b;
-
- while (isBoxSymbolic(t,a,b)) {
- x = makeParSchema(x, generateInputSlotSchema(a));
- t = b;
- }
- return makeSeqSchema(x, generateDiagramSchema(t));
-}
-