Exporting 3D to SVG

I have some code snippets that allow me to export normal 2d drawings to svg output. Today I made a simple 3d drawing. I want to flatten and export it to an svg. I tried using my 2d boilerplate but it doesn’t work.

What happens is that it ouputs an svg with multiple layers, as expected, but all the svgs are empty. I’m guessing this is because I’m in 3D not 2D.

#https://py.processing.org/tutorials/transform2d/
#https://py.processing.org/tutorials/p3d/

add_library('svg')
from xml.etree import ElementTree
OUTPUT_FN = '.\\combined.svg'

CANVAS = (1000,1000)
CUBE_COUNT = 5
SCALING = 1.5
ROTATE_X, ROTATE_Y, ROTATE_Z = (-PI/11, PI/8, 0)
BOX_SIZE = 30



def settings():
    pass
    
def setup():
    noLoop()
    global x,y,z
    size(CANVAS[0],CANVAS[1],P3D)
    x=width/2
    y=height/2
    z = 0
    
def draw():
    
    noFill()
    translate(x,y,z)
    rotateX(ROTATE_X)
    rotateY(ROTATE_Y)
    rotateZ(ROTATE_Z)

    svgs = []
    for n in range(CUBE_COUNT):
        fn = "{0}_box.svg".format(n)
        svgs.append(fn)
        beginRecord(SVG,fn)
        box(BOX_SIZE)
        scale(2,2,2)    
        endRecord()

    # create an empty combined.svg file
    beginRecord(SVG, OUTPUT_FN); endRecord()
    ElementTree.register_namespace('', 'http://www.w3.org/2000/svg')
    tree = ElementTree.parse(OUTPUT_FN)
    combined = tree.getroot()


    # add the (circles/squares) svgs to the combined.svg file
    for svg in svgs:
        file = ElementTree.parse(svg).getroot()
        group = ElementTree.SubElement(combined, 'g')
        group.set('id', svg)
        group.set('inkscape:groupmode', 'layer')
        group.set('inkscape:label', svg)
        for child in file.getchildren():
            group.append(child)
    
    # save the changes to the combined.svg file
    tree.write(OUTPUT_FN)
    
    print("done")

You can see the output svg is just an empty container of layers:

<svg xmlns="http://www.w3.org/2000/svg" height="1000" style="fill-opacity:1; color-rendering:auto; color-interpolation:auto; text-rendering:auto; stroke:black; stroke-linecap:square; stroke-miterlimit:10; shape-rendering:auto; stroke-opacity:1; fill:black; stroke-dasharray:none; font-weight:normal; stroke-width:1; font-family:'Dialog'; font-style:normal; stroke-linejoin:miter; font-size:12px; stroke-dashoffset:0; image-rendering:auto;" width="1000"><defs id="genericDefs" /><g /><g id="0_box.svg" inkscape:groupmode="layer" inkscape:label="0_box.svg"><defs id="genericDefs" /><g /></g><g id="1_box.svg" inkscape:groupmode="layer" inkscape:label="1_box.svg"><defs id="genericDefs" /><g /></g><g id="2_box.svg" inkscape:groupmode="layer" inkscape:label="2_box.svg"><defs id="genericDefs" /><g /></g><g id="3_box.svg" inkscape:groupmode="layer" inkscape:label="3_box.svg"><defs id="genericDefs" /><g /></g><g id="4_box.svg" inkscape:groupmode="layer" inkscape:label="4_box.svg"><defs id="genericDefs" /><g /></g></svg>

Have you tried this approach?

size(500, 500, P3D);
beginRaw(SVG, 'output.svg')
translate(250, 250)
rotateZ(0.2)
rotateY(0.2)
box(100)
endRaw()

Note, however, that everything is constructed from triangles.

2 Likes

image

yeah, turns out it worked ok. Not sure why your cube shows triangles forming the sides – as neither my Processing output, nor the svg (in inkscape) shows them (fortunately).

Thanks for the hint on beginRaw() – now I need to figure out if I can separate the different cubes into the different layers like I do when I use beginRecord().

The triangles are invisible. In Inkscape, select View > Display Mode > Outline and you’ll see them.

I suspect that you intend plotting this? In which case, the triangles will show up in the plot. If that’s an issue, you could remove (programmatically) all of the paths in the SVG that have no stroke – i.e. anything with an attribute of style="stroke:none;"

2 Likes

Huh, strange. Even in Inkscape 1.0 when I goto View::Display_mode::Outline, I never see the triangle ‘cross-members’. (which is a good thing!)

And when I print to my AxiDraw plotter, I also just get the edges of the wireframe (the result I want) and no triangle cross-members.

Interesting! Could you tell me if the SVG source code contains any elements with an attribute of style="stroke:none;"? I’m wondering if this is a Processing- or Inkscape-related difference.

Here is the svg output of my program.

For reference, Inkscape is version 1.0 (4035a4fb49, 2020-05-01) and Processing 3.5.4 with Python Mode 3063.

This is the code I used:

#2D Transformations \ Tutorials
#P3D \ Tutorials

add_library(‘svg’)
from xml.etree import ElementTree
OUTPUT_FN = ‘.\combined.svg’

CANVAS = (1000,1000)
CUBE_COUNT = 5
SCALING = 1.5
ROTATE_X, ROTATE_Y, ROTATE_Z = (-PI/11, PI/8, 0)
BOX_SIZE = 30

def settings():
pass

def setup():
noLoop()
global x,y,z
size(CANVAS[0],CANVAS[1],P3D)
x=width/2
y=height/2
z = 0

def draw():

noFill()
translate(x,y,z)
rotateX(ROTATE_X)
rotateY(ROTATE_Y)
rotateZ(ROTATE_Z)

svgs = []
for n in range(CUBE_COUNT):
    fn = "{0}_box.svg".format(n)
    svgs.append(fn)
    #beginRecord(SVG,fn)
    beginRaw(SVG,fn)
    box(BOX_SIZE)
    scale(2,2,2)    
    #endRecord()
    endRaw()
    
# create an empty combined.svg file
beginRecord(SVG, OUTPUT_FN); endRecord()
ElementTree.register_namespace('', 'http://www.w3.org/2000/svg')
tree = ElementTree.parse(OUTPUT_FN)
combined = tree.getroot()


# add the (circles/squares) svgs to the combined.svg file
for svg in svgs:
    file = ElementTree.parse(svg).getroot()
    group = ElementTree.SubElement(combined, 'g')
    group.set('id', svg)
    group.set('inkscape:groupmode', 'layer')
    group.set('inkscape:label', svg)
    for child in file.getchildren():
        group.append(child)

# save the changes to the combined.svg file
tree.write(OUTPUT_FN)

print("done")
2 Likes

See here: https://stylecampaign.com/blog/2014/03/3d-to-svg/

2 Likes