X-Git-Url: https://scm.cri.ensmp.fr/git/Faustine.git/blobdiff_plain/1059e1cc0c2ecfa237406949aa26155b6a5b9154..66f23d4fabf89ad09adbd4dfc15ac6b5b2b7da83:/interpreter/preprocessor/faust-0.9.47mr3/compiler/draw/drawschema.cpp diff --git a/interpreter/preprocessor/faust-0.9.47mr3/compiler/draw/drawschema.cpp b/interpreter/preprocessor/faust-0.9.47mr3/compiler/draw/drawschema.cpp new file mode 100644 index 0000000..6e3624c --- /dev/null +++ b/interpreter/preprocessor/faust-0.9.47mr3/compiler/draw/drawschema.cpp @@ -0,0 +1,634 @@ +/************************************************************************ + ************************************************************************ + 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "boxes.hh" +#include "ppbox.hh" +#include "prim2.hh" + +#include +#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 gPendingExp; // Expressions that need to be drawn +static set 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 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 "-svg/" or + *"-ps/" depending of . + */ +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 into a + * legal file name. The resulting file name is stored in + * a table of at least chars. Returns the 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 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)); +} +