A Quark or a Plug-in visualising SynthDef

Hi @prko,

here a solution suggested to me by Fredrik Olofsson and today updated in his blog . . Fredrik Olofsson Musikproduktion . . f0blog one-reason-why-i-love-sc.

To use the sc3-dot quark in SuperCollider on a mac you’ll need to install graphviz, preferably via macports (sudo port install graphviz) or via homebrew (brew install graphviz).

Then install the quark from within SuperCollider…

Quarks.install("sc3-dot");

add the following lines to your startup.scd file…

Dot.renderMode= 'pdf';
Dot.pdfViewer= "open -a Preview.app";
Dot.dotCmd= "/opt/local/bin/dot";  //macports
//Dot.dotCmd= "/usr/local/bin/dot";  //homebrew

and finally recompile SuperCollider. Now a command like…

{SinOsc.ar}.draw;

should automatically create a .dot file, convert to .pdf and open using Preview.app.

By default you will find the resulting .dot and .pdf files in the ~/Library/Application Support/SuperCollider/tmp directory.

Thanks again Fredrik! :heart:

2 Likes

Awesome. I feel like I have installed this 20 times in the past 20 years and then lose the installation.

BTW - the dot executable may be in a different place on M1 with homebrew. Mine is here:

Dot.dotCmd= "/opt/homebrew/bin/dot";

Sam

@MurbiX and @Sam_Pluta

Thanks!

@rdd
It is suggested that the following steps (or similar to these) be considered for inclusion in the help file, at your discretion. In my experience, the process for letting your quark sc3-dot in modern SC on macOS was longer than in Ubuntu. I apologise if my inquiry comes across as impolite, that was not my intention.

"brew install graphviz".runInTerminal // step 1

Quarks.install("https://gitlab.com/rd--/sc3-dot") // step 2

thisProcess.recompile // step 3

(
Dot.renderMode= 'pdf';
Dot.pdfViewer= "open -a Preview.app";
Dot.dotCmd= "/opt/homebrew/bin/dot";
{SinOsc.ar}.draw
)

(
Dot.renderMode= 'svg';
{SinOsc.ar}.draw
)
1 Like

Yes, ta kindly F0!

I’ve added a link to the MacOS instructions in the Readme.

Hopefully that helps people in the future.

Ps. The windows default probably did (does?) work if you had (have?) Cygwin setup? I don’t really know!

1 Like

Pps. And no, not at all impolite, apologies slow reply!

1 Like

Thanks for this!

I tried to do it in Windows, but I still do not know how to do it. No idea, sorry!

@rdd

I looked into it again over several hours. I’ve found one sure and one suspected reason why it doesn’t work on Windows.

  1. The “\” or "/"is not delivered correctly.
    I found this by modifying .unixCmd with .runInTerminal in line 38:
    sc/Dot.sc · master · r d / sc3-dot · GitLab

  2. The .shellQuote seems to send the filename in single quotes, but the Windows command prompt only accepts double quotes.

Ta kindly for looking! Super happy to accept and fixes you come across, and I’m sorry I can’t be of more help with this…

1 Like

I finally found a way to make scDot work in Windows. Using it on Mac and Linux is now easier!
Please have a look at the following video:
https://www.dropbox.com/scl/fi/4oa5j370danjkunreeusq/Dot-simple.mov?rlkey=msvrr3bzp97icrto0zsdq08b6&dl=0

This is available after revising some method definitions for the Dot class as follows (I just added some preliminary code to skip some steps and added the correct implementation for Windows… It would be great if you could reflect this! I currently only use github… Or I can submit a PR and ask for a merge…):

Dot {

	classvar <>directory, <>dotCmd, <>dotViewer, <>svgViewer, <>pdfViewer, <>renderMode, <>drawInputName, <>useSplines, <>useTables, <>fontSize, <>fontName, <>fixEdgeLocation, <>verbose, <>truncateInputsAt;

	*defaultSettings {
		directory = Platform.defaultTempDir;
		dotCmd = "dot";
		dotViewer = nil;
		svgViewer = nil;
		pdfViewer = nil;
		renderMode = 'dot'; // dot | svg | pdf | hsc3
		drawInputName = false;
		useSplines = false;
		useTables = true;
		fontSize = 10;
		fontName = "Helvetica";
		fixEdgeLocation = false;
		verbose = false;
		truncateInputsAt = 32
	}

	*initClass {
		this.defaultSettings
	}

	*baseFileNameFor { | synthDef |
		^directory.standardizePath +/+ synthDef.name.asString;
	}

	*fileNameForWithExtension { | synthDef extension |
		^Dot.baseFileNameFor(synthDef) ++ extension;
	}

	*asyncCmd { | message command |
		verbose.if {
			["asyncCmd", message, command].postln
		};
		command.unixCmd // asynchronous
	}

	*syncCmd { | message command |
		verbose.if {
			["syncCmd", message, command].postln
		};
		command.systemCmd // synchronous
	}

	*viewDotFile { | fileName |
		var viewerDefault = (osx: "open", linux: "dot -Txlib", windows: "start" + "".quote + dotCmd.quote);
		var viewer = dotViewer ? viewerDefault.at(thisProcess.platform.name);
		var command = if(thisProcess.platform.asString != "a WindowsPlatform") {
			"% %".format(viewer, fileName.shellQuote)
		} {
			"% %".format(viewer, fileName)
		};
		Dot.asyncCmd("viewDotFile", command)
	}

	*viewDotFileFor { | synthDef |
		Dot.viewDotFile(Dot.fileNameForWithExtension(synthDef, ".dot"))
	}

	*dotFileToSvgFileFor { | synthDef |
		var dotFileName = Dot.fileNameForWithExtension(synthDef, ".dot");
		var svgFileName = Dot.fileNameForWithExtension(synthDef, ".svg");
		var command = if(thisProcess.platform.asString != "a WindowsPlatform") {
			"% -Tsvg % -o %".format(dotCmd, dotFileName.shellQuote, svgFileName.shellQuote)
		} {
			"% -Tsvg % -o %".format(dotCmd, dotFileName, svgFileName)
		};
		Dot.syncCmd("dotFileToSvgFileFor", command)
	}

	*viewSvgFileFor { | synthDef |
		svgViewer.notNil.if {
			var command = "% %".format(svgViewer, Dot.fileNameForWithExtension(synthDef, ".svg").shellQuote);
			Dot.asyncCmd("viewSvgFileFor", command)
		} {
			var image = Image.openSVG(Dot.fileNameForWithExtension(synthDef, ".svg"));
			var window = Window.new(synthDef.name.asString, Rect(100, 100, image.bounds.width, image.bounds.height), false, true, nil, false);
			window.onClose_ {
				image.free
			};
			window.view.setBackgroundImage(image, 1, 1, nil);
			window.view.fixedSize_(Size.new(width: image.width, height: image.height));
			window.front
		}
	}

	*dotFileToPdfFileFor { | synthDef |
		var dotFileName = Dot.fileNameForWithExtension(synthDef, ".dot");
		var pdfFileName = Dot.fileNameForWithExtension(synthDef, ".pdf");
		var command = if(thisProcess.platform.asString != "a WindowsPlatform") {
			"% -Tpdf % -o %".format(dotCmd, dotFileName.shellQuote, pdfFileName.shellQuote)
		} {
			"% -Tpdf % -o %".format(dotCmd, dotFileName, pdfFileName)
		};
		Dot.syncCmd("dotFileToPdfFileFor", command)
	}

	*viewPdfFileFor { | synthDef |
		var viewerDefault = (osx: "open", linux: "xdg-open", windows: "start" + "".quote + "C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe".quote);
		var viewer = pdfViewer ? viewerDefault.at(thisProcess.platform.name);
		var command = if(thisProcess.platform.asString != "a WindowsPlatform") {
			"% %".format(viewer, Dot.fileNameForWithExtension(synthDef, ".pdf").shellQuote)
		} {
			"% %".format(viewer, Dot.fileNameForWithExtension(synthDef, ".pdf"))
		};
		Dot.asyncCmd("viewPdfFileFor", command)
	}

	*drawHsc3 { | synthDef |
		var command = if(thisProcess.platform.asString != "a WindowsPlatform") {
			"hsc3-dot scsyndef-draw %".format(Dot.fileNameForWithExtension(synthDef, ".scsyndef").shellQuote)
		} {
			"hsc3-dot scsyndef-draw %".format(Dot.fileNameForWithExtension(synthDef, ".scsyndef"))
		};
		synthDef.writeDefFile(directory.standardizePath,true);
		Dot.asyncCmd("drawHsc3", command)
	}

	*writeDotFileFor { | synthDef |
		var file = File(Dot.fileNameForWithExtension(synthDef, ".dot"), "w");
		synthDef.dot(file);
		file.close
	}

	*draw { | synthDef |
		renderMode.switch(
			'hsc3', {
			    Dot.drawHsc3(synthDef)
			},
			'dot', {
			    Dot.writeDotFileFor(synthDef);
			    Dot.viewDotFileFor(synthDef)
			},
			'svg', {
			    Dot.writeDotFileFor(synthDef);
			    Dot.dotFileToSvgFileFor(synthDef);
			    Dot.viewSvgFileFor(synthDef)
			},
			'pdf', {
			    Dot.writeDotFileFor(synthDef);
			    Dot.dotFileToPdfFileFor(synthDef);
			    Dot.viewPdfFileFor(synthDef)
			},
			{
				"Dot.draw: unknown renderMode".error
			}
		)
	}

}
1 Like

If you want to skip installing anything, there are several decent browser-based dot renderers:
https://viz-js.com/

It’s very easy to hook this one up inside of a WebView so you can get dot graphs directly in your SuperCollider UI - I did this at some point though, for the life of me, I can’t find that code anymore!

1 Like

Thanks for that! I needed something like this for other work!