An introduction to Sketch 3D for PGF and TikZ users

  • Published 2007-02-25 (4 years, 11 months ago)
  • Updated 2007-03-25 (4 years, 10 months ago)

More then a year ago I wrote enthusiastically about Sketch 3D, a tool for creating simple 3D scenes for use with (La)TeX. Originally it generated PSTricks code, but recently the author added support for PGF and TikZ. In this notebook entry I will give a brief introduction to this great tool. Note that you have to read the Sketch manual to actually learn Sketch. I will only cover aspects specific to PGF and TikZ.

Contents

An introductory example

I'll start with this simple pyramid scene:

To draw the pyramid I have to open a text editor and write the following code:

def O (0,0,0) % origo
def J [0,1,0] % rotation axis
def dx 2.3
def dy 2.5
def dz dx

def axes {
    % draw the axes
    def ax (dx,0,0)
    def ay (0,dy,0)
    def az (0,0,dz)
    line[arrows=<->,line width=.4pt](ax)(O)(ay)
    line[arrows=->,line width=.4pt](O)(az)
    % annote axes
    special |\path #1 node[left] {$z$}
                   #2 node[below] {$x$}
                   #3 node[above] {$y$};|(az)(ax)(ay)
}

def pyramid {
    def p0 (0,2)
    def p1 (1.5,0)
    def N 4
    def seg_rot rotate(360 / N, [J])
    % draw the pyramid by rotating a line about the J axis
    sweep[cull=false,fill=blue!20]
        { N, [[seg_rot]] } line(p0)(p1)
    {axes}
}

put { view((10,4,2)) } {pyramid}

global { language tikz }

To get the final drawing I have to process the above code with Sketch and TeX. Saving the code as pyramid.sk, I can generate the drawing with:

> sketch -T pyramid.sk > pyramid.tex
> pdflatex pyramid.tex

The first command creates the TikZ code. The -T option puts the code inside a predefined template, making it a standalone document. Without the option, Sketch will only output the drawing code.

A more interesting result is achieved by making the pyramid faces transparent and adding a red floor to the pyramid:

sweep[fill=red!20] { N<>, [[seg_rot]] }
    line[cull=false,fill=blue!20,fill opacity=0.5](p0)(p1)

The above example shows two of the major benefits of using PGF and TikZ with Sketch:

  • The output can be compiled directly with pdfTeX
  • Support for transparency

How it works

Sketch is a command line tool written by Eugene Ressler. It is written in standard C and is easily built on any platform with a C compiler. A binary is provided for Windows users.

Sketch's job is to process 3D scenes, make a 2D projection and generate a list of corresponding TikZ or PSTricks commands. Sketch accepts a small domain-specific language, designed for creating 3D scenes. With Sketch you can easily do things like affine arithmetics, transformations, sweeps and extrusions. Operations that are very hard or impossible to do with TeX.

A crucial part of Sketch is the projection of 3D objects to 2D. The classic depth sort algorithm (also known as painter's algorithm) is used to determine which polygons are shown . The algorithm also involves splitting overlapping polygons and it is useful to understand what this means in practice. I'll illustrate this with the following example of two intersecting polygons.

def scene{
    polygon[fill=red!20,draw=red]
        (0,0,1)(1,0,0)(0,1,0)
    put {rotate(25)} {polygon[fill=blue!20,draw=blue]
        (0,0,1)(1,0,0)(0,1,0)}
}

put { scale(2) then view((5,4,8)) } {scene}
global { language tikz }

The result is two intersecting polygons.

The generated code looks like this:

\filldraw[fill=red!20,draw=none](-.976,-.61)--(1.561,-.381)--(.608,.887)--cycle;
\draw[draw=red](-.976,-.61)--(1.561,-.381)--(.608,.887);
\filldraw[fill=blue!20,draw=blue](-.976,-.61)--(1.415,.371)--(-.66,1.697)--cycle;
\filldraw[fill=red!20,draw=none](-.976,-.61)--(.608,.887)--(0,1.695)--cycle;
\draw[draw=red](.608,.887)--(0,1.695)--(-.976,-.61);

To draw the figure, five different paths are drawn:

Note that the red triangle is split into two different polygons and two line segments, while the blue triangle is drawn using a single filldraw operation. This is the depth sort and polygon splitting in action.

PGF/TikZ options

Stroke and fill parameters are set using the well known option syntax:

line[arrows=<->,draw=red,line width=ultra thick](0,0,0)(1,1,1)
polygon[fill=lightgray,style=semitransparent](0,0,1)(1,0,0)(0,1,0)

From the example in the previous section, it is evidently that Sketch needs to know the difference between fill and stroke parameters. The PGF/TikZ parameters recognized by Sketch are listed in the TikZ/PGF options section of the manual.

Custom styles

TikZ makes it easy to mix stroke and fill parameters in a style definition. This makes it a bit difficult for Sketch. If you want to use custom styles, you have to use the fill style and line style options. This way Sketch knows which styles to copy when doing polygon and line splitting.

line[arrows=<->,line style=mylinestyle](0,0,0)(1,1,1)
polygon[fill style=myfillstyle](0,0,1)(1,0,0)(0,1,0)

It is up to you not to mix fill and stroke parameters. Mixing them may give some weird results.

Specials

Sketch allows you to embed raw TeX and PGF/TikZ code using special objects. This way you can easily annotate you drawings. The formal syntax is

special |raw_text|[lay=lay_value] point_list

where || can be any character. The points specified in point_list are available for use in raw_text, transformed to two dimensional coordinates. An example will hopefully make things clearer:

% Vector cross product
def O (0,0,0) % origin
def theta 60 % angle between U and V
def U 2*[1,0,0]
def V rotate(theta)*[U]
% Define styles
% The use of the [lay=under] option forces the code to be outputted
% at the beginning of the generated code. The default behavior is to
% output special at the end.
special |\tikzstyle{vector}=[-latex',very thick]
            \tikzstyle{angle}=[->,shorten >=1pt]|[lay=under]


def scene {
    def u (O)+[U]
    def v (O)+[V]
    def w (O)+[U]*[V]
    line[line style=vector] (O)(u)
    line[line style=vector] (O)(v)
    line[line style=vector] (O)(w)
    special |\path #1 node[below] {$\vec{u}$}
                   #2 node[below] {$\vec{v}$}
                   #3 node[above] {$\vec{u}\times\vec{v}$};|(u)(v)(w)
    % Annotate theta angle. Sketch currently does not support
    % curves. Use a curve path instead, with appropriate computed
    % startpoint, midpoint and endpoint.
    def s (O) + 0.5*[U]
    def m rotate(theta/2)*(s)
    def e rotate(theta)*(s)
    special |\draw[angle] #1..controls #2.. node[midway,below] {$\theta$}#3;|
        (s)(m)(e)
}

put { view((2,0.5,2),(O),[0,0,1]) } {scene}
global { language tikz }

The special command is really powerful, and my favourite Sketch feature.

Tips and tricks

Some random tips and tricks. Will probably be moved to a separate notebook entry.

Read the manual carefully
The manual is well written with many excellent examples. You save yourself from some frustration by reading the manual carefully.
Faces with equal depth are drawn arbitrary
The depth sort algorithm gets into trouble when faces have the same depth. A workaround is to offset one of the faces with a very small value. The offset is too small to be visible, but it will affect in which order the faces are drawn. Example:
def ee 0.000001
polygon[fill=red] (0.5,0,ee)(0,0.5,ee)(-0.5,0,ee)
polygon[fill=blue] (1,0,0)(0,1,0)(-1,0,0)

Here's another example:

def ee 0.000001
def jR 1.5/2
def jR_inner 0.5/2
def jH 2/2
def joint put{rotate(-15,[1,0,0])} {
    def n 20
    sweep[fill=blue!20, cull=false, draw=gray]{n<>, rotate(360/n,[0,1,0])}
        line[draw=gray,fill opacity=0.8,cull=false,fill=blue!20]
            (jR,-jH/2)(jR,jH/2)
    sweep[fill=darkgray, cull=false]{n<>, rotate(360/n,[0,1,0])}
        line(jR_inner,-jH/2-ee)(jR_inner,jH/2+ee)
}

{joint}
global { language tikz }

The left cylinder is drawn without the small offset ee. In this case the depth sort algorithm hides the inner cylinder. The result may be different if another view angle is chosen. Using a small offset for the inner cylinder solves this problem.

Document templates
Sketch's default behaviour is to only output drawing code. To compile the code you'll need some boiling plate code. Sketch ships with a default template which you activate with the -T option. You can easily create your own template which you specify with the -t filename option. Sketch will look in the template for the %%SKETCH_OUTPUT%% string and replace it with the code needed to draw the scene. Example template:
\documentclass{article}
\usepackage[x11names,rgb]{xcolor}
\usepackage{tikz}
\usetikzlibrary{snakes,arrows,shapes,backgrounds}
\usepackage{amsmath}

% Use preview.sty to crop the page
\usepackage[active,tightpage]{preview}
\PreviewEnvironment{tikzpicture}
\setlength\PreviewBorder{2pt}%

\begin{document}
%%SKETCH_OUTPUT%%
\end{document}
Report bugs and feature requests to the author
Sketch's TikZ support is new and there are probably bugs. Please report any issues to the author of Sketch. Remember that feedback from users is motivating.
Experiment with the view angle
Finding the right view angle often requires some trial and error.
Specials are affected by the view angle
Changing the view angle also changes the position of specials. You should therefore wait with fine tuning until you have found an view angle you are satisfied with.

Examples

Below is a small collection of different drawings made with Sketch. Click on them to see the source code. The truncated cone example is from the Sketch manual. Note that some of the examples were made while I started to use Sketch, so the code may not be of the best quality.

Comments

  • #1 S M Mahbub Murshed, March 19, 2007 at 8:10 p.m.

    Nice tool! I was wondering how steep the learning curve would be! Are you working on this as part of you PhD?

  • #2 Kjell Magne Fauske, March 19, 2007 at 10:20 p.m.

    @Mahub

    The learning curve is probably a bit steep, but if you are used to working with LaTeX, PGF/TIkZ or PSTricks it should be no problem to learn Sketch. It always takes some time to learn a new language.

    I'm not working on this as part of my PhD. I have used Sketch to create some illustrations related to my research topic though (like for instance the Super short baseline positioning figure above). I'm just fascinated by graphics. Sketch is a great tool that deserves a broader audience.

  • #3 Helmer Aslaksen, March 23, 2007 at 8:40 a.m.

    Thanks for sharing this! I've been looking for something like this for a long time.

    Tusen takk!

    Helmer

  • #4 Gene Ressler, March 25, 2007 at 7:26 p.m.

    Hello! I'm the author of sketch.

    I thank Kjell Magne Fauske for this excellent page. He has exactly captured the intent of sketch.

    I'm currently (slowly) designing a major upgrade. If you have ideas, send them to sketch@frontiernet.net .

    On the current list:

    • 3d clipping
    • Simple lighting models and smooth-shaded surfaces
    • "for loop" list comprehensions to draw any parametrically defined surface (including f(x,y) plots)
    • List of _ types and associated semantics for arithmetic.
    • Full lisp semantics for defs - parameters and defs as first class objects (i.e. closures)

    Thanks, Gene

  • #5 Mirko Vukovic, April 4, 2007 at 8:53 p.m.

    I have a hate/love relation with sketch. But at the end of the day, the latter always prevails, and makes me forget the former.

    It is the web sites like these that help the latter prevail :-)

    Thanks to both you and Gene.

    Mirko

  • #6 Kjell Magne Fauske, April 4, 2007 at 9:53 p.m.

    @Mirko Thanks for the comment. I think I can understand your hate/love relation to Sketch. Creating a complex drawing can be very time consuming, but for me, the great looking results make it worth it.

  • #7 S M Mahbub Murshed, April 11, 2007 at 10:54 p.m.

    @ Ressler A great language tool should have a great library of functions or classes. From the users perspective it will be nice to have a set of predefined set of objects that we can instantiate with some parameters.

  • #8 Lakedaemon, April 17, 2007 at 5:18 p.m.

    now..if Sketch had a .obj importer, we could use these zillions free 3D model to illustrate our documents with TeX ^_^

  • #9 Kjell Magne Fauske, April 17, 2007 at 8:50 p.m.

    @Lakedaemon

    Funny that you mention it. A long time ago wrote a simple script for exporting meshes from Blender to Sketch. Blender can import obj files, so this should be a way to import 3D models for use with Sketch. Here is an experiment I did:

    Suzanne

    When I have some spare time I will finish the exporter and make it available for download.

  • #10 Gene Ressler, July 9, 2007 at 6:08 a.m.

    Those who are looking for examples of the kinds of things that Sketch is useful for might look at this very nice application, depicting flight dynamics.

    http://www.dpa.unina.it/demarco/work/quaternion_doc.pdf

    It's due to

    Agostino De Marco, PhD Assistant Professor Università degli Studi di Napoli Federico II / University of Naples

  • #11 Kjell Magne Fauske, July 9, 2007 at 3:52 p.m.

    @Gene Wow! Those drawings are really impressive. Very inspiring.

  • #12 Agostino, July 13, 2007 at 8:32 p.m.

    Thanks Gene for posting one of my examples. And thanks to Kjell Magne Fauske for this great website!

  • #13 michele, August 9, 2007 at 5:15 p.m.

    Sketch is powerful and well documented, however I had not accessed it without a clever and effective tutorial as those from Fauske. He knows the wait to teach things by example, and with simple yet effective ones. thank you all

  • #14 Ian Mackenzie, August 16, 2007 at 5:42 p.m.

    First of all, thanks Gene for an excellent program, and thanks Kjell for a couple great articles about it! I just found Sketch a few days ago after a long and frustrating search for, well, exactly what Sketch is. (By the way, those improvements sound great, especially the generic lists and improved defs.)

    One trick I've found (possibly PGF/TikZ-specific - I've never used PSTricks) has to do with labelling axes - instead of using 'above', 'below' etc. in separate commands, draw the line itself inside a 'special' command and use the 'pos' option to place the label. For instance:

    def axes
    {
        def length 1.5
        def origin (0, 0, 0)
        def p1 (length, 0, 0)
        def p2 (0, length, 0)
        def p3 (0, 0, length)
        special | \footnotesize \draw[arrows=->] #1 -- #2 node[pos=1.2] {$x$}; | (origin)(p1)
        special | \footnotesize \draw[arrows=->] #1 -- #2 node[pos=1.2] {$y$}; | (origin)(p2)
        special | \footnotesize \draw[arrows=->] #1 -- #2 node[pos=1.2] {$z$}; | (origin)(p3)
    }
    

    Using pos=1.2 centers the label just off the end of the axis, so it looks nicer and you don't have to worry about view orientation.

  • #15 Kjell Magne Fauske, August 16, 2007 at 8:16 p.m.

    @Ian Thank you for the tip Ian. Really useful. Never thought about using values > 1 on the pos option before.

    To save some typing, you can put everything inside one special statement like this:

    special |\draw[->,font=\footnotesize,pos=1.2] 
        #1 edge node {$x$} #2
           edge node {$y$} #3
           edge node {$z$} #4; | (origin)(p1)(p2)(p3)
    
  • #16 Agostino, September 1, 2007 at 6:57 p.m.

    I am really looking forward for a python script that might enable Blender users to export meshes for use with Sketch.

    Kjell: I am available to collaborate on this kind of things.

  • #17 Kjell Magne Fauske, September 1, 2007 at 8 p.m.

    @Agostino

    A Blender->Sketch exporter is indeed useful. I am currently brushing up my Blender scripting skills, and I will bump up the priority on the exporter.

  • #18 Gene Ressler, September 20, 2007 at 1:43 a.m.

    Hey Kjell, your nice example seems to have inspired an improvement to PSTricks -- transparent colors.

    http://tug.org/PSTricks/main.cgi?file=pst-plot/3D/examples#coor

  • #19 Kjell Magne Fauske, September 20, 2007 at 8:55 a.m.

    @Gene

    That's cool. Nice to see that PSTricks borrows some ideas from PGF and TikZ.

  • #20 Kjell Magne Fauske, October 18, 2007 at 1:06 p.m.

    I have now written a simple Blender to Sketch exporter: Blend2Sketch

  • #21 Ian Mackenzie, April 2, 2008 at 9:09 p.m.

    I was toying around with the idea of writing a little Qt/OpenGL viewer for Sketch - the idea would be to try to parse an input file and display a 3D version that could be manipulated. It wouldn't be complete by any sense (the 'special' command would likely be ignored, for instance), but would allow you to quickly see what you're creating and view it from different angles - really just a visualization tool. Thoughts? Is this something that anybody else has worked on?

  • #22 Kjell Magne Fauske, April 2, 2008 at 9:39 p.m.

    @Ian Great idea! Could be very useful.

    Not sure if parsing a Sketch file is enough though. To actually render the 3D geometry you probably have to evaluate the various Sketch statements. Example:

    put {rotate(25)} {polygon[fill=red,draw=blue]
        (0,0,1)(1,0,0)(0,1,0)}
    

    To render the above code you have to apply a rotation to the polygon coordinates. Not difficult per se, but requires more than just parsing the tokens.

    The easiest solution is probably to modify the Sketch source code so that you can access the raw 3D geometry in some way.

    A somewhat related project is my Blender2Sketch tool that generates Sketch code from Blender objects.

  • #23 Gene, April 23, 2008 at 3:41 a.m.

    Ian's idea is great. I thought about doing it myself, but decided this is not something I'd like to support. Multiple platforms are not fun.

    Gtk might be easier than OpenGL in that regard.

    To follow Kjell's advice, look at emit.c. It's pretty simple to add a new output "language." In this case your output is messages to the graphical window.

    Sketch still maintains options in the format of the output language. Therefore to get colors, line styles, etc. you have to interpret pstricks or TikZ option strings or else invent your own new option sublanguage. The last choice is a little harder because you must deal with option list splitting, which is explained in the comments of opts.c.

    This leads me to the idea I was going to implement: Make a PSTricks (or TikZ) previewer. This is of course more generally useful than a viewer for Sketch only.

    With this you can make a Sketch previewer as just a little driver program or shell script.

  • #24 Kjell Magne Fauske, April 24, 2008 at 8:31 a.m.

    Ian's idea is great. I thought about doing it myself, but decided this is not something I'd like to support. Multiple platforms are not fun. Gtk might be easier than OpenGL in that regard.

    Another possibility is to modify Sketch to output directly to a 3D file format like for instance VRML/X3D, OBJ and U3D. You could then use a 3d-viewer to look at the output. I'm not very familiar with those formats, but the OBJ format looks very simple.

    This leads me to the idea I was going to implement: Make a PSTricks (or TikZ) previewer. This is of course more generally useful than a viewer for Sketch only. With this you can make a Sketch previewer as just a little driver program or shell script.

    Great idea. For TikZ I know about one previewer, ktikz. Never tried it though.

  • #25 Cristóvão, June 20, 2008 at 4:30 p.m.

    I made a highlight shcem .xml file for Sketch language to KDE Kate.

    http://www.isr.uc.pt/~crisjss/sketch.xml

    If anyone try it please say something.

  • #26 Kjell Magne Fauske, June 23, 2008 at 9:49 p.m.

    Comments temporarily disabled due to massive spamming.

Comments are disabled for this entry