Permalink
Browse files

Scruffy is now not a gitmodule anymore since there's a problem with g…

…h-pages build system
  • Loading branch information...
1 parent 80a2ea5 commit 1724ef3b07840d55910b7f6c4523c67afcea8b38 @timjb committed Apr 11, 2011
View
@@ -1,3 +0,0 @@
-[submodule "scruffy"]
- path = scruffy
- url = http://github.com/aivarsk/scruffy.git
Submodule scruffy deleted from 28995e
View
@@ -0,0 +1 @@
+Makes SVG shapes look hand-drawn and creates UML diagrams using yUML (http://yuml.me) syntax
View
@@ -0,0 +1,32 @@
+Makes SVG shapes look hand-drawn and creates UML diagrams using yUML (http://yuml.me) syntax
+
+Requires dot (http://www.graphviz.org/) and rsvg-convert (http://librsvg.sourceforge.net/), has been tested on Ubuntu.
+
+./yuml2dot.py --png "[User|+Forename;+Surname;+HashedPassword;-Salt|+Login();+Logout()]" > samples/sample13.png
+
+.. image:: https://github.com/aivarsk/scruffy/raw/master/samples/sample13.png
+
+./yuml2dot.py --png --font-family Purisa --scruffy "[User|+Forename;+Surname;+HashedPassword;-Salt|+Login();+Logout()]" > samples/sample13-scruffy.png
+
+.. image:: https://github.com/aivarsk/scruffy/raw/master/samples/sample13-scruffy.png
+
+./yuml2dot.py --png "[note: You can stick notes on diagrams too!{bg:cornsilk}],[Customer]<>1-orders 0..*>[Order], [Order]++*-*>[LineItem], [Order]-1>[DeliveryMethod], [Order]*-*>[Product], [Category]<->[Product], [DeliveryMethod]^[National], [DeliveryMethod]^[International]" > samples/sample14.png
+
+.. image:: https://github.com/aivarsk/scruffy/raw/master/samples/sample14.png
+
+./yuml2dot.py --png --font-family Purisa --scruffy "[note: You can stick notes on diagrams too!{bg:cornsilk}],[Customer]<>1-orders 0..*>[Order], [Order]++*-*>[LineItem], [Order]-1>[DeliveryMethod], [Order]*-*>[Product], [Category]<->[Product], [DeliveryMethod]^[National], [DeliveryMethod]^[International]" > samples/sample14-scruffy.png
+
+.. image:: https://github.com/aivarsk/scruffy/raw/master/samples/sample14-scruffy.png
+
+yUML++:
+
+Class name (string till the first pipe) is the unique block identifier not the whole text inside square brackets. So [classA|field1;field2] and [classA] is the same block. Fields and methdods from the first occurance are used.
+
+[classA|field1;field2]-[BoxX]
+[classA]-[BoxY]
+
+Initial cluster support, cluster specification must be the last.
+
+./yuml2dot.py --png --font-family Purisa --scruffy "[Node A]->[Node B],[Node B]->[Node C],[Group [Node A][Node B]]" > samples/sample15-scruffy.png
+
+.. image:: https://github.com/aivarsk/scruffy/raw/master/samples/sample15-scruffy.png
View
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+./yuml2dot.py --png "[User]" > samples/sample01.png
+./yuml2dot.py --png "[Customer]->[Billing Address]" > samples/sample02.png
+./yuml2dot.py --png "[Customer]1-0..*[Address]" > samples/sample03.png
+./yuml2dot.py --png "[Order]-billing[Address], [Order]-shipping[Address]" > samples/sample04.png
+./yuml2dot.py --png "[❝Customer❞{bg:orange}]❶- ☂>[Order{bg:green}]" > samples/sample05.png
+./yuml2dot.py --png "[Company]<>-1>[Location], [Location]+->[Point]" > samples/sample06.png
+./yuml2dot.py --png "[Company]++-1>[Location]" > samples/sample07.png
+./yuml2dot.py --png "[Customer]<>1->*[Order], [Customer]-[note: Aggregate Root{bg:cornsilk}]" > samples/sample08.png
+./yuml2dot.py --png "[Wages]^-[Salaried], [Wages]^-[Contractor]" > samples/sample09.png
+./yuml2dot.py --png "[<<ITask>>]^-.-[NightlyBillingTask]" > samples/sample10.png
+./yuml2dot.py --png "[日本語]->[Köttbullar]" > samples/sample11.png
+./yuml2dot.py --png "[HttpContext]uses -.->[Response]" > samples/sample11.png
+./yuml2dot.py --png "[<<IDisposable>>;Session]" > samples/sample12.png
+./yuml2dot.py --png "[User|+Forename;+Surname;+HashedPassword;-Salt|+Login();+Logout()]" > samples/sample13.png
+./yuml2dot.py --png "[note: You can stick notes on diagrams too!{bg:cornsilk}],[Customer]<>1-orders 0..*>[Order], [Order]++*-*>[LineItem], [Order]-1>[DeliveryMethod], [Order]*-*>[Product], [Category]<->[Product], [DeliveryMethod]^[National], [DeliveryMethod]^[International]" > samples/sample14.png
+./yuml2dot.py --png "[Node A]->[Node B],[Node B]->[Node C],[Group [Node A][Node B]]" > samples/sample15.png
+
+./yuml2dot.py --png --font-family Purisa --scruffy "[User]" > samples/sample01-scruffy.png
+./yuml2dot.py --png --font-family Purisa --scruffy "[Customer]->[Billing Address]" > samples/sample02-scruffy.png
+./yuml2dot.py --png --font-family Purisa --scruffy "[Customer]1-0..*[Address]" > samples/sample03-scruffy.png
+./yuml2dot.py --png --font-family Purisa --scruffy "[Order]-billing[Address], [Order]-shipping[Address]" > samples/sample04-scruffy.png
+./yuml2dot.py --png --font-family Purisa --scruffy "[❝Customer❞{bg:orange}]❶- ☂>[Order{bg:green}]" > samples/sample05-scruffy.png
+./yuml2dot.py --png --font-family Purisa --scruffy "[Company]<>-1>[Location], [Location]+->[Point]" > samples/sample06-scruffy.png
+./yuml2dot.py --png --font-family Purisa --scruffy "[Company]++-1>[Location]" > samples/sample07-scruffy.png
+./yuml2dot.py --png --font-family Purisa --scruffy "[Customer]<>1->*[Order], [Customer]-[note: Aggregate Root{bg:cornsilk}]" > samples/sample08-scruffy.png
+./yuml2dot.py --png --font-family Purisa --scruffy "[Wages]^-[Salaried], [Wages]^-[Contractor]" > samples/sample09-scruffy.png
+./yuml2dot.py --png --font-family Purisa --scruffy "[<<ITask>>]^-.-[NightlyBillingTask]" > samples/sample10-scruffy.png
+./yuml2dot.py --png --font-family Purisa --scruffy "[日本語]->[Köttbullar]" > samples/sample11-scruffy.png
+./yuml2dot.py --png --font-family Purisa --scruffy "[HttpContext]uses -.->[Response]" > samples/sample11-scruffy.png
+./yuml2dot.py --png --font-family Purisa --scruffy "[<<IDisposable>>;Session]" > samples/sample12-scruffy.png
+./yuml2dot.py --png --font-family Purisa --scruffy "[User|+Forename;+Surname;+HashedPassword;-Salt|+Login();+Logout()]" > samples/sample13-scruffy.png
+./yuml2dot.py --png --font-family Purisa --scruffy "[note: You can stick notes on diagrams too!{bg:cornsilk}],[Customer]<>1-orders 0..*>[Order], [Order]++*-*>[LineItem], [Order]-1>[DeliveryMethod], [Order]*-*>[Product], [Category]<->[Product], [DeliveryMethod]^[National], [DeliveryMethod]^[International]" > samples/sample14-scruffy.png
+
+./yuml2dot.py --png --font-family Purisa --scruffy "[Node A]->[Node B],[Node B]->[Node C],[Group [Node A][Node B]]" > samples/sample15-scruffy.png
+
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
View
@@ -0,0 +1,208 @@
+#!/usr/bin/env python
+# Copyright (C) 2011 by Aivars Kalvans <aivars.kalvans@gmail.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+# Makes SVG shapes look hand-drawn like "scruffy" UML diagrams:
+# http://yuml.me/diagram/scruffy/class/samples
+#
+# Adds new points (with slight offsets) between existing points.
+# Changes font to ..?
+# Adds shadows to polygons
+# Adds gradient
+
+import sys
+import math
+import random
+import xml.etree.ElementTree as etree
+
+def parsePoints(points):
+ points = points.split()
+ return [(float(x), float(y)) for x, y in [point.split(',') for point in points]]
+
+def lineLength(p1, p2):
+ dx = p2[0] - p1[0]
+ dy = p2[1] - p1[1]
+ return math.sqrt(dx * dx + dy * dy)
+
+def splitLine(p1, p2, l):
+ ''' find point on line l points away from p1 '''
+ dx = p2[0] - p1[0]
+ dy = p2[1] - p1[1]
+ lp = math.sqrt(dx * dx + dy * dy)
+ ax = dx / lp
+ ay = dy / lp
+ return (p1[0] + l * ax, p1[1] + l * ay)
+
+def frandrange(start, stop):
+ ''' random.randrange for floats '''
+ start, stop = int(start * 10.0), int(stop * 10.0)
+ r = random.randrange(start, stop)
+ return r / 10.0
+
+SVG_NS = 'http://www.w3.org/2000/svg'
+def ns(tag):
+ return '{%s}%s' % (SVG_NS, tag)
+
+def transformRect2Polygon(elem):
+ pass
+
+def transformLine2Polyline(elem):
+ elem.tag = ns('polyline')
+ elem.points = '%(x1)s,%(y1)s %(x2)s,%(y2)s' % elem.attrib
+ for key in ('x1', 'x2', 'y1', 'y2'): del elem.attrib[key]
+
+def transformPolyline(elem):
+ points = parsePoints(elem.attrib['points'])
+ newPoints = []
+ for i in xrange(len(points) - 1):
+ p1, p2 = points[i], points[i + 1]
+
+ newPoints.append(p1)
+ l = lineLength(p1, p2)
+ if l > 10:
+ p = splitLine(p1, p2, frandrange(4, l - 4))
+ newPoints.append((
+ p[0] + frandrange(0.5, 2) * random.choice([1, -1]),
+ p[1] + frandrange(0.5, 2) * random.choice([1, -1])
+ ))
+
+ newPoints.append(points[-1])
+
+ elem.attrib['points'] = ' '.join(['%f,%f' % p for p in newPoints])
+
+_usedColors = {}
+
+def transformPolygon(elem):
+ transformPolyline(elem)
+ fill = elem.get('fill', '')
+ if fill == 'none':
+ elem.attrib['fill'] = 'white'
+
+def transformText(elem, font):
+ elem.attrib['font-family'] = font
+
+def transformAddShade(root, elem):
+ if elem.get('fill', '') == 'white' and elem.get('stroke', '') == 'white':
+ # Graphviz puts everything in one big polygon. Skip it!
+ return
+
+ # Need to prepend element of the same shape
+ shade = root.makeelement(elem.tag, elem.attrib)
+ for i, child in enumerate(root):
+ if child == elem:
+ root.insert(i, shade)
+ break
+
+ shade.attrib['fill'] = '#999999'
+ shade.attrib['stroke'] = '#999999'
+ shade.attrib['stroke-width'] = shade.attrib.get('stroke-width', '1')
+ shade.attrib['transform'] = 'translate(4, 4)'
+ shade.attrib['style'] = 'opacity:0.75;filter:url(#filterBlur)'
+
+def transformAddGradient(elem):
+ if elem.get('fill', '') == 'white' and elem.get('stroke', '') == 'white':
+ # Graphviz puts everything in one big polygon. Skip it!
+ return
+
+ fill = elem.get('fill', '')
+ if fill == 'none':
+ elem.attrib['fill'] = 'white'
+ elif fill != 'black':
+ _usedColors[fill] = True
+ elem.attrib['style'] = 'fill:url(#' + fill + ');' + elem.attrib.get('style', '')
+
+def _transform(root, options, level=0):
+ for child in root[:]:
+ if child.tag == ns('rect'):
+ transformRect2Polygon(child)
+ elif child.tag == ns('line'):
+ transformLine2Polyline(child)
+
+ if child.tag == ns('polygon'):
+ transformPolygon(child)
+ transformAddShade(root, child)
+ transformAddGradient(child)
+ elif child.tag == ns('path'):
+ #transformAddShade(root, child)
+ pass
+ elif child.tag == ns('polyline'):
+ transformPolyline(child)
+ #see class diagram - shade of inside line
+ #transformAddShade(root, child)
+ elif child.tag == ns('text'):
+ if options.font:
+ transformText(child, options.font)
+
+ _transform(child, options, level + 1)
+
+ if level == 0:
+ defs = root.makeelement(ns('defs'), {})
+ root.insert(0, defs)
+ filterBlur = etree.SubElement(defs, ns('filter'), {'id': 'filterBlur'})
+ etree.SubElement(filterBlur, ns('feGaussianBlur'), {'stdDeviation': '0.69', 'id':'feGaussianBlurBlur'})
+ for name in _usedColors:
+ gradient = etree.SubElement(defs, ns('linearGradient'), {'id': name, 'x1':"0%", 'xy':"0%", 'x2':"100%", 'y2':"100%"})
+ etree.SubElement(gradient, ns('stop'), {'offset':'0%', 'style':'stop-color:white;stop-opacity:1'})
+ etree.SubElement(gradient, ns('stop'), {'offset':'50%', 'style':'stop-color:%s;stop-opacity:1' % name})
+
+def transform(fin, fout, options):
+ '''
+ Read svg from file object fin, write output to file object fout
+
+ options.png (boolean) Try to produce PNG output
+ options.font (string) Font family to use (Ubuntu: Purisa)
+ '''
+ root = etree.parse(fin).getroot()
+ _transform(root, options)
+
+ scruffySvg = etree.tostring(root) + '\n'
+
+ if options.png:
+ import subprocess
+ subprocess.Popen(['rsvg-convert', '-f', 'png'], stdin=subprocess.PIPE, stdout=fout).communicate(input=scruffySvg)
+ else:
+ fout.write(scruffySvg)
+
+def main():
+ import optparse
+
+ parser = optparse.OptionParser(usage='usage: %prog [options] [input file]')
+ parser.add_option('-p', '--png', action='store_true', dest='png',
+ help='create a png file (requires rsvg-convert)')
+ parser.add_option('-o', '--output', action='store', dest='output',
+ help='output file name')
+ parser.add_option('--font-family', action='store', dest='font',
+ help='output file name')
+ (options, args) = parser.parse_args()
+
+ if len(args) > 1:
+ parser.error('Too many arguments')
+
+ fin, fout = sys.stdin, sys.stdout
+ if options.output:
+ fout = open(options.output, 'wb')
+
+ if len(args) > 0:
+ fin = open(args[0])
+
+ transform(fin, fout, options)
+
+if __name__ == '__main__':
+ main()
Oops, something went wrong.

0 comments on commit 1724ef3

Please sign in to comment.