| Version 31 (modified by bousch, 4 years ago) |
|---|
Pic2Plot plugin
This plugin will convert the PIC Graphical Language to an image. See http://en.wikipedia.org/wiki/Pic_language for more info on the PIC language. You can use this to create all sorts of freeform diagrams.
You can also use this to write UML sequence diagrams if you get the sequence.pic file from the UMLGraph package at http://www.umlgraph.org/. This is described here.
You can also use this plugin to create custom shapes to be used with Graphviz. The SuperFormula PIC script below (see Examples) can generate all sorts of images based on a number of parameters.
Prerequisites
You will need pic2plot which is included in GNU PlotUtils
The output image has a large canvas. If you want to crop this you will also need ImageMagick
The plugin has an options panel where you must configure the following settings:
- path to the pic2plot executable
- path to ImageMagick folder
Examples
This image is drawn by the code below it:
Note: this image is created with the crop parameter
[:pic:''' .PS box "box"; move; line "line" ""; move; arrow "arrow" ""; move; circle "circle"; move; ellipse "ellipse"; move; arc; down; move; "arc" .PE ''']
Arguments
The plugin supports passing arguments to pic2plot. The arguments appendix has to start with 'args=' and must be completely quoted. The following example will make the image larger, and use a specified font:
[:pic:''' .PS arrow "ow!" .PE ''';'args=-F HersheySans --bitmap-size 1000x1000']
Crop
If you want to crop the whitespace around the picture you can do it like this:
[:pic:''' .PS arrow "ow!" .PE ''';crop]
You can combine this with arguments:
[:pic:''' .PS arrow "ow!" .PE ''';'args=-F HersheySans --bitmap-size 1000x1000';crop]
Super Formula
The script below contains a macro for the SuperFormula and is used to create the image above:
[:pic:///
.PS 7
# Custom formula shapes
#
# sf2d(a, b, m, n1, n2, n3, [periods, [transformation, [rotation]]])
#
# the following transformations are possible
# 0: None
# 1: x
# 2: sin(x)
# 3: cos(x)
# 4: sqrt(x)
# 5: sin(x)²
# 6: cos(x)²
# 7: 1+sin(x)²
# 8: 1+cos(x)²
# 9: atan(x)
#
define sf2d {
pi = atan2(0,-1)
a = $1
b = $2
m = $3
n1 = $4
n2 = $5
n3 = $6
start = 0
if ("$7" == "") then {
stop = 2 * pi
periods = 1024
} else {
periods = $7
stop = 2048/periods * pi
}
step = pi / periods
if ("$8" == "") then {transformation = 0} else {transformation = $8}
if ("$9" == "") then {degrees = 0} else {degrees = $9}
S: [
for phi = start to stop by step do {
if transformation == 1 then { f = phi } else {
if transformation == 2 then { f = sin(phi) } else {
if transformation == 3 then { f = cos(phi) } else {
if transformation == 4 then { f = sqrt(phi) } else {
if transformation == 5 then { f = sin(phi)^2 } else {
if transformation == 6 then { f = cos(phi)^2 } else {
if transformation == 7 then { f = 1+sin(phi)^2 } else {
if transformation == 8 then { f = 1+cos(phi)^2 } else {
if transformation == 9 then { f = atan2(phi, 1) } else {
f = 1
}}}}}}}}}
r1 = cos(phi*m/4)/a
if (r1 < 0) then {r1 = -r1};
r2 = sin(phi*m/4)/b
if (r2 < 0) then {r2 = -r2};
r = (r1^n2 + r2^n3)^(1/n1)
if (r == 0) then {
x = 0
y = 0
} else {
r = f/r
x = r * cos(phi)
y = r * sin(phi)
}
# rotation
if (degrees != 0) then {
angle = degrees * pi/180
xOld = x
yOld = y
x = (xOld * cos( angle )) - (yOld * sin( angle ))
y = (xOld * sin( angle )) + (yOld * cos( angle ))
}
if (phi == start) then {
move to (x, -y)
}
line to (x, -y)
}
]
}
sf2d(1, 1, 12, -2, 1, 1); {"generating" at last [].c}; move;
sf2d(3, 2, 4, 30, 15, 15); {"Superformula" at last [].c}; move;
sf2d(1, 1, 4, 3, 1, 1); {"shapes" at last [].c}; move;
sf2d(1, 1, 8, 1, 1, 8); {"is" at last [].c}; move;
sf2d(1, 1, 8, 12, -3, 8); {"FUN" at last [].c};
.PE
///;"args=-F HersheySans -f .02 --bitmap-size 1000x1000";crop]
Some other examples:
UML sequence diagram
[:pic:''' .PS copy "\full\path\to\sequence.pic"; # Define the objects object(O,"o:Toolkit"); placeholder_object(P); step(); # Activation and messages active(O); message(O,O,"callbackLoop()"); create_message(O,P,"p:Peer"); message(O,P,"handleExpose()"); active(P); return_message(P,O,""); inactive(P); destroy_message(O,P); inactive(O); # Complete the lifeline of O step(); complete(O); .PE ''';'args=-F HersheySans --bitmap-size 1000x1000';crop]
A more complex example from the UMLGraph manual:
[:pic:'''
# usage-example for the comment extensions to the
# Written by Sebastian Setzer
.PS
copy "\full\path\to\sequence.pic";
# Define the objects
actor(U, "");
object(G,"g:GUI");
placeholder_object(Dummy1); # more space
placeholder_object(D);
step();
# Message sequences
active(G);
step();
create_message(G,D,"db:DB");
active(D);
step();
inactive(D);
async(); # use asynchrone messages (not-filled arrowheads)
comment(D,C,down 1 right, wid 1 ht 0.7 "waiting for" "condition" "dbMailbox")
message(U,G,"openBrowser");
message(G,D,"query()"); active(D);
message(D,G,"result"); inactive(D);
connect_to_comment(D,C)
message(U,G,"scroll");
message(G,D,"query()"); active(D);
message(D,G,"result"); inactive(D);
connect_to_comment(D,C)
message(U,G,"Exit");
step();
begin_frame(G,F,"OnExit");
message(G,D,"shutdown()"); inactive(G); active(D);
comment(G,C,down .2 right .2, wid 2 ht 0.25 "wait for cond. dbShutdown")
step();
comment(D,C,right, wid 2 ht 1 \
"all queries preceeding the" \
"shutdown in the mailbox" \
"are answered already." \
"DbQuery-Objects can" \
"be destroyed")
message(D,G,"done"); inactive(D); active(G);
sync();
destroy_message(G,D,"");
step();
end_frame(D,F);
step();
inactive(G);
# Complete the lifelines
step();
complete(G);
complete(U);
.PE
''';'args=-F HersheySans --bitmap-size 1000x1000';'crop']
Have fun!
Sourcecode
Save the code below to a file called pic2plot.py in your user_extensions folder in the wikidpad program folder.
"""
2009-05-25: changed argument pattern
2009-05-20: created by bousch_AT_gmail.com
Do with it what you want, no warranty whatsoever.
"""
import os, os.path
from subprocess import list2cmdline
import time
import wx
from pwiki.TempFileSet import createTempFile
from pwiki.StringOps import mbcsEnc, mbcsDec, lineendToOs
WIKIDPAD_PLUGIN = (("InsertionByKey", 1), ("Options", 1))
LOG = False
def describeInsertionKeys(ver, app):
"""
API function for "InsertionByKey" plugins
Returns a sequence of tuples describing the supported
insertion keys. Each tuple has the form (insKey, exportTypes, handlerFactory)
where insKey is the insertion key handled, exportTypes is a sequence of
strings describing the supported export types and handlerFactory is
a factory function (normally a class) taking the wxApp object as
parameter and returning a handler object fulfilling the protocol
for "insertion by key" (see EqnHandler as example).
ver -- API version (can only be 1 currently)
app -- wxApp object
"""
return (
(u"pic", ("html_single", "html_previewWX", "html_preview", "html_multi"), P2PHandler),
)
class P2PHandler:
"""
Base class fulfilling the "insertion by key" protocol.
"""
def __init__(self, app):
self.app = app
self.extAppExe = None
def taskStart(self, exporter, exportType):
"""
This is called before any call to createContent() during an
export task.
An export task can be a single HTML page for
preview or a single page or a set of pages for export.
exporter -- Exporter object calling the handler
exportType -- string describing the export type
Calls to createContent() will only happen after a
call to taskStart() and before the call to taskEnd()
"""
# Find Gnuplot executable by configuration setting
self.extAppExe = self.app.getGlobalConfig().get("main",
"plugin_pic2plot_exePath", "")
self.imExtAppExe = self.app.getGlobalConfig().get("main",
"plugin_imagemagick_dirPath", "")
def taskEnd(self):
"""
Called after export task ended and after the last call to
createContent().
"""
pass
def createContent(self, exporter, exportType, insToken):
"""
Handle an insertion and create the appropriate content.
exporter -- Exporter object calling the handler
exportType -- string describing the export type
insToken -- insertion token to create content for (see also
PageAst.Insertion)
An insertion token has the following member variables:
key: insertion key (unistring)
value: value of an insertion (unistring)
appendices: sequence of strings with the appendices
Meaning and type of return value is solely defined by the type
of the calling exporter.
For HtmlXmlExporter a unistring is returned with the HTML code
to insert instead of the insertion.
"""
if not insToken.value:
# Nothing in, nothing out
return u""
if self.extAppExe == "":
# No path to pic2plot executable -> show message
return u"<pre>" + _(u"[Please set path to pic2plot executable]") +\
u"</pre>"
if self.imExtAppExe == "":
hasImageMagick = False
else:
hasImageMagick = True
crop = False
# Get exporters temporary file set (manages creation and deletion of
# temporary files)
tfs = exporter.getTempFileSet()
pythonUrl = (exportType != "html_previewWX")
dstFullPath = tfs.createTempFile("", ".png", relativeTo="")
url = tfs.getRelativeUrl(None, dstFullPath, pythonUrl=pythonUrl)
baseDir = os.path.dirname(exporter.getMainControl().getWikiConfigPath())
# find arguments in appendices
pattern = 'args='
arguments = ''
for a in insToken.appendices:
if a.startswith(pattern):
arguments = str(a[len(pattern):])
if a == 'crop' and hasImageMagick:
crop = True
# Retrieve quoted content of the insertion
#bstr = lineendToOs(mbcsEnc(insToken.value, "replace")[0])
# windows lineendings confuse pic2plot
bstr = mbcsEnc(insToken.value, "replace")[0]
# Store token content in a temporary file
srcfilepath = createTempFile(bstr, ".pic")
try:
cmdline = '%s %s -T png %s > %s' % (self.extAppExe, arguments, srcfilepath, dstFullPath)
# Run external application
#childIn, childOut, childErr = os.popen3(cmdline, "b")
from subprocess import Popen, PIPE
p = Popen([cmdline], shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=False)
p.wait() # Will wait for process to end
childIn, childOut, childErr = (p.stdin, p.stdout, p.stderr)
cropcmd = ''
if crop:
cropcmd = '%s\convert -trim %s %s' % (self.imExtAppExe, dstFullPath, dstFullPath)
p2 = Popen([cropcmd], shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=False)
p2.wait()
childIn2, childOut2, childErr2 =(p2.stdin, p2.stdout, p2.stderr)
if u"noerror" in [a.strip() for a in insToken.appendices]:
childErr.read()
errResponse = ""
else:
errResponse = childErr.read()
finally:
os.unlink(srcfilepath)
# Open logfile
if LOG:
f = open(baseDir + '\pic2plot.log', 'a')
f.write(time.strftime('\n%d-%m-%Y %H:%M:%S\n'))
f.write("Current dir: %s\n" % os.getcwd())
f.write("Commandline: %s\n" % cmdline)
f.write(time.strftime('\n%d-%m-%Y %H:%M:%S') + ' : ' + errResponse)
f.write("Crop: %s\n" % cropcmd)
for a in insToken.appendices:
appendices = a.strip().split("---")
for app in appendices:
f.write(app.strip() + "\n")
f.close()
if errResponse != "":
errResponse = mbcsDec(errResponse, "replace")[0]
return u"<pre>" + _(u"[pic2plot error: %s]") % errResponse +\
u"</pre>"
# Return appropriate HTML code for the image
if exportType == "html_previewWX":
# Workaround for internal HTML renderer
return (u'<img src="%s" border="0" align="bottom" alt="pic2plot" />'
u' ') % url
else:
return u'<img src="%s" border="0" align="bottom" alt="pic2plot" />' \
% url
def getExtraFeatures(self):
"""
Returns a list of bytestrings describing additional features supported
by the plugin. Currently not specified further.
"""
return ()
def registerOptions(ver, app):
"""
API function for "Options" plugins
Register configuration options and their GUI presentation
ver -- API version (can only be 1 currently)
app -- wxApp object
"""
# Register option
app.getDefaultGlobalConfigDict()[("main", "plugin_pic2plot_exePath")] = u""
app.getDefaultGlobalConfigDict()[("main", "plugin_imagemagick_dirPath")] = u""
# Register panel in options dialog
app.addOptionsDlgPanel(Pic2plotOptionsPanel, u" pic2plot")
class Pic2plotOptionsPanel(wx.Panel):
def __init__(self, parent, optionsDlg, app):
"""
Called when "Options" dialog is opened to show the panel.
Transfer here all options from the configuration file into the
text fields, check boxes, ...
"""
wx.Panel.__init__(self, parent)
self.app = app
pt = self.app.getGlobalConfig().get("main", "plugin_pic2plot_exePath", "")
self.tfPath = wx.TextCtrl(self, -1, pt)
mainsizer = wx.BoxSizer(wx.VERTICAL)
inputsizer = wx.BoxSizer(wx.HORIZONTAL)
inputsizer.Add(wx.StaticText(self, -1, _(u"Path to pic2plot:")), 0,
wx.ALL | wx.EXPAND, 5)
inputsizer.Add(self.tfPath, 1, wx.ALL | wx.EXPAND, 5)
mainsizer.Add(inputsizer, 0, wx.EXPAND)
pt = self.app.getGlobalConfig().get("main", "plugin_imagemagick_dirPath", "")
self.ifPath = wx.TextCtrl(self, -1, pt)
inputsizer = wx.BoxSizer(wx.HORIZONTAL)
inputsizer.Add(wx.StaticText(self, -1, _(u"Path to ImageMagick folder:")), 0,
wx.ALL | wx.EXPAND, 5)
inputsizer.Add(self.ifPath, 1, wx.ALL | wx.EXPAND, 5)
mainsizer.Add(inputsizer, 0, wx.EXPAND)
self.SetSizer(mainsizer)
self.Fit()
def setVisible(self, vis):
"""
Called when panel is shown or hidden. The actual wxWindow.Show()
function is called automatically.
If a panel is visible and becomes invisible because another panel is
selected, the plugin can veto by returning False.
When becoming visible, the return value is ignored.
"""
return True
def checkOk(self):
"""
Called when "OK" is pressed in dialog. The plugin should check here if
all input values are valid. If not, it should return False, then the
Options dialog automatically shows this panel.
There should be a visual indication about what is wrong (e.g. red
background in text field). Be sure to reset the visual indication
if field is valid again.
"""
return True
def handleOk(self):
"""
This is called if checkOk() returned True for all panels. Transfer here
all values from text fields, checkboxes, ... into the configuration
file.
"""
pt = self.tfPath.GetValue()
self.app.getGlobalConfig().set("main", "plugin_pic2plot_exePath", pt)
pt = self.ifPath.GetValue()
self.app.getGlobalConfig().set("main", "plugin_imagemagick_dirPath", pt)
Attachments
-
pic_example.png
(0.9 kB) - added by bousch
4 years ago.
pic example
-
sequence_diagram.png
(1.9 kB) - added by bousch
4 years ago.
Sequence diagram
-
complex_sequence.png
(9.3 kB) - added by bousch
4 years ago.
More complex sequence diagram
-
superformula.png
(4.3 kB) - added by bousch
4 years ago.
superformula
-
test.png
(5.5 kB) - added by bousch
4 years ago.
Superformula sf2d(1, 1, 5.3, 0.4, 0, 6, 0, 64);
-
sun.png
(4.0 kB) - added by bousch
4 years ago.
Superformula sf2d(1, 1, 50, 6, 15, 15);
-
cog.png
(2.0 kB) - added by bousch
4 years ago.
Superformula sf2d(1, 0.6, 30, 75, 1.5, 35);
-
star_wire.png
(3.0 kB) - added by bousch
4 years ago.
Superformula sf2d(1, 1, 5, 3, 6, 6, 128, 1, 18);
-
stick.png
(0.5 kB) - added by bousch
4 years ago.
stickfigure
-
pic-example-diagram.png
(3.0 kB) - added by bousch
4 years ago.
Simple diagram








