Import DMX Fixtures from CSV / SVG
  • mad-matt
    garageCube team
    Posts: 1475
    Joined: Mon Sep 09, 2013 5:50 pm

    Import DMX Fixtures from CSV / SVG

    by mad-matt » Mon Nov 14, 2022 2:49 pm

    MadMapper 5.2.0 added new possibilities for importing fixtures from CSV, you can now set your fixture name and group hierarchy in a CSV file column. For instance "Stage Fixtures/Fixture 1" will end making a fixture called "Fixture 1" in a group called "Stage Fixtures". If the group doesn't yet exists, it will create it.
    Also fixtures will be placed in the order you created it in the CSV (first fixture in the CSV will be on top of all others in the fixtures list in MadMapper)

    An example CSV is attached. Here is the content:
    Fixture Definition Name;Start Universe;Start Channel;StartX;StartY;EndX;EndY;Width;Fixture Name (optional)
    Generic - Pixel RGB;0;1;0;0.5;2;0.5;1;Group-1/Pixel-1
    Generic - Pixel RGB;0;4;2;0.5;4;0.5;1;Group-1/Pixel-2
    Generic - Pixel RGB;0;7;4;0.5;6;0.5;1;Group-1/Pixel-3
    Generic - Pixel RGB;0;10;6;0.5;8;0.5;1;Group-2/Pixel-4
    Generic - Pixel RGB;0;13;8;0.5;10;0.5;1;Group-2/Pixel-5
    Generic - Pixel RGB;0;16;10;0.5;12;0.5;1;Group-2/Pixel-6
    Generic - Pixel RGB;0;19;12;0.5;14;0.5;1;Group-2/SubGroup-1/Pixel-7
    Generic - Pixel RGB;0;22;14;0.5;16;0.5;1;Group-2/SubGroup-1/Pixel-8
    Generic - Pixel RGB;0;25;16;0.5;18;0.5;1;Group-2/SubGroup-1/Pixel-9
    The first field is the Fixture definition name, which can either contain the fixture definition group or not (we could have written "Pixel RGB" in this case)
    Then comes start universe & start channel. You must know that or refer to MadLight documentation.
    StartX, StartY, EndX, EndY & Width will be used to position the fixture. All values are in pixels. There are two cases here: fixture line or other type of fixture (quad or circle). For quad or circle fixtures, the input rectangle will be placed with top left at (StartX,StartY) and bottom left at (EndX, EndY) & Width is ignored. For Fixture Lines, the line will start at (StartX,StartY) and end at (EndX,EndY), the width parameter will define line width.
    Then comes Fixture Name column which was described above. If a fixture with the same name already exists, we'll append a number.

    SVG import hasn't changed in 5.2.0. The idea here is to extract only the SVG Line primitives and create a fixture for each. It is possible to add fixture name & DMX patching on each SVG Line primitive as following:
    <line fixtureDefinition="Generic - 60x1L" fixtureUniverse="0" fixtureChannel="1" stroke="#000000" x1="65" x2="7" y1="7" y2="65" />
    An example is provided as attachment.

    Please post here if you have any question. We might bundle better documentation in a PDF.
    Attachments
    FixturesCsvDemo.svg.zip
    (4.14 KiB) Downloaded 182 times
    test import fixture lines 2x1.csv.zip
    (1.06 KiB) Downloaded 175 times
  • fekril
    member
    Posts: 37
    Joined: Mon Dec 06, 2010 7:04 pm

    Re: Import DMX Fixtures from CSV / SVG

    by fekril » Thu Apr 13, 2023 1:30 pm

    Great, but what's the point, we're trying to understand how this function could improve our production flow, we can't find it, could you give us a concrete example for which this function improves the production flow or its quality of realization
  • mad-matt
    garageCube team
    Posts: 1475
    Joined: Mon Sep 09, 2013 5:50 pm

    Re: Import DMX Fixtures from CSV / SVG

    by mad-matt » Fri Apr 14, 2023 12:28 pm

    If you have hundreds of LED bars, you might prefer to handle the patching of each with formulas in an excel sheet;
    If you have a complex structure, you might want to place lines in Illustrator and use SVG format.
    We once did a project with more than 400 bars, we used a python script to generate an SVG that includes DMX patching, quite handy. The visuals were created from the same SVG so everything matches perfectly.
    This one was smaller but same principle, same SVG for fixtures and for content

    Screenshot 2023-04-14 at 12.26.40.png
    Screenshot 2023-04-14 at 12.26.40.png (842.13 KiB) Viewed 3777 times
    Screenshot 2023-04-14 at 12.27.10.png
    Screenshot 2023-04-14 at 12.27.10.png (518.83 KiB) Viewed 3777 times
  • fekril
    member
    Posts: 37
    Joined: Mon Dec 06, 2010 7:04 pm

    Re: Import DMX Fixtures from CSV / SVG

    by fekril » Sun Apr 16, 2023 10:43 am

    ok, thank's , you said "We once did a project with more than 400 bars, we used a python script to generate an SVG that includes DMX patching, quite handy,"

    can you share with us your python script to generate an SVG that includes the DMX patch?
  • mad-matt
    garageCube team
    Posts: 1475
    Joined: Mon Sep 09, 2013 5:50 pm

    Re: Import DMX Fixtures from CSV / SVG

    by mad-matt » Mon Apr 17, 2023 11:30 am

    Sure

    Here is a complex python script that generates the SVG for this project: https://www.instagram.com/p/BNg9xQOhktu/
    It adds DMX patching & some data in SVG XML elements because it was loaded by a handmade generator doing particules.
    It also generates an OBJ file for 3D preview
    import xml.etree.cElementTree as ET
    import math
    import sys, getopt

    # Final setup values
    countX = 14
    countY = 11

    pixelsPerBar = 59
    hardwarePitch = 3
    exportPitch = 3
    targetFile = "bars.svg"
    exportText = False

    # Set exportPitch to hardwarePitch for the file to import fixture from
    # Set exportPitch to zero for the file we use in Le Metropol module

    argv = sys.argv[1:]

    try:
    opts, args = getopt.getopt(argv,"hto:p:e:",[])
    except getopt.GetoptError:
    print 'generate_svg.py -o <outputfile> -p <hardwarePitch> -e <exportPitch> -t'
    print 'examples'
    print ' python generate_svg.py -o fixtures.svg -p 3 -e 3 -t'
    print ' python generate_svg.py -o bars.svg -p 3 -e 0'
    sys.exit(2)
    for opt, arg in opts:
    if opt in ("-o"):
    targetFile = arg
    elif opt in ("-t"):
    exportText = True
    elif opt in ("-p"):
    hardwarePitch = int(arg)
    elif opt in ("-e"):
    exportPitch = int(arg)

    gridSizeX = countX * 2
    gridSizeY = countY

    margin = 4

    resX = gridSizeX * (pixelsPerBar + hardwarePitch*2)
    resY = gridSizeY * (pixelsPerBar + hardwarePitch*2)

    svg = ET.Element("svg", version="1.1", width=str(resX+hardwarePitch*2 + 2*margin), height=str(resY-hardwarePitch*2 + 2*margin))

    def getPointPos(x,y):
    return [margin + x * (pixelsPerBar + hardwarePitch*2), margin + y * (pixelsPerBar + hardwarePitch*2)]

    def getPoint3DPos(x,y):
    rotAngle = 2 * 3.141592654 * x / gridSizeX;
    return [gridSizeX*(pixelsPerBar + hardwarePitch*2) * math.cos(rotAngle), y * (pixelsPerBar + hardwarePitch*2), gridSizeX*(pixelsPerBar + hardwarePitch*2) * math.sin(rotAngle)]

    # Each bar extremity point has a number so we can compute connections between bars
    def getPointId(x,y):
    if x >= gridSizeX:
    return getPointId(0,y)
    else:
    return x/2 + y*countX

    patchingLines=[[[-1,10],[0,9],[-1,8],[-1,6],[0,5],[-1,4],[-1,2],[0,1],[-1,0]],[[0,11],[-1,10],[-1,8],[0,7],[-1,6],[-1,4],[0,3],[-1,2],[-1,0]],[[0,11],[0,9],[1,8],[0,7],[0,5],[1,4],[0,3],[0,1],[1,0]],[[0,11],[1,10],[0,9],[0,7],[1,6],[0,5],[0,3],[1,2],[0,1]]]

    def getArtNetPatchForSegment(x1,y1,x2,y2):
    if x1%2 == 0:
    baseX = x1
    elif x2%2 == 0:
    baseX = x2
    else:
    assert (x1==x2), "Should be a vertical on odd numbers"
    baseX = x1 + 1

    relativeX1 = x1 - baseX
    relativeX2 = x2 - baseX
    baseUniverse = ((baseX%gridSizeX) / 2) * 4
    for line in range(0,4):
    patchingLine = patchingLines[line]
    for idInLine in range(0,len(patchingLine)-1):
    patchP1 = patchingLine[idInLine]
    patchP2 = patchingLine[idInLine+1]

    universe = baseUniverse+line
    if universe >= 16:
    universe += 24-16
    if universe >= 24+20:
    universe += 24-20

    if patchP1[0] == relativeX1 and patchP1[1] == y1 and patchP2[0] == relativeX2 and patchP2[1] == y2:
    return {"universe":universe, "channel":1+idInLine * 60,"reversed":False}
    if patchP1[0] == relativeX2 and patchP1[1] == y2 and patchP2[0] == relativeX1 and patchP2[1] == y1:
    return {"universe":universe, "channel":1+idInLine * 60,"reversed":True}

    print("Error: patching not found for " + str(x1) + "," + str(y1) + " - " + str(x2) + "," + str(y2))
    print(" relative: " + str(relativeX1) + "," + str(y1) + " - " + str(relativeX2) + "," + str(y2))
    print(" baseX: " + str(baseX))
    return {}

    # Create the SVG file
    for y in range(0,gridSizeY+1):
    for x in range(0,gridSizeX+1):
    if x % 2 != y % 2:
    # 3 lines going down from this point
    segStartId = getPointId(x,y)

    if y < gridSizeY and x > 0: # Down left
    segStart = getPointPos(x,y)
    segStart[0] -= exportPitch
    segStart[1] += exportPitch
    segEnd = getPointPos(x-1,y+1)
    segEndId = getPointId(x-1,y+1)
    segEnd[0] += exportPitch
    segEnd[1] -= exportPitch
    segmentId = segStartId * 3 + 0;

    artNetPatch=getArtNetPatchForSegment(x,y,x-1,y+1)

    if artNetPatch["reversed"]:
    ET.SubElement(svg, "line", x2=str(segStart[0]), y2=str(segStart[1]), x1=str(segEnd[0]), y1=str(segEnd[1]), fill="none", stroke="#000000", fixtureDefinition="Generic - 60x1L", id="l"+str(segmentId), endPointId=str(segStartId), startPointId=str(segEndId), fixtureUniverse=str(artNetPatch["universe"]), fixtureChannel=str(artNetPatch["channel"]))
    else:
    ET.SubElement(svg, "line", x1=str(segStart[0]), y1=str(segStart[1]), x2=str(segEnd[0]), y2=str(segEnd[1]), fill="none", stroke="#000000", fixtureDefinition="Generic - 60x1L", id="l"+str(segmentId), startPointId=str(segStartId), endPointId=str(segEndId), fixtureUniverse=str(artNetPatch["universe"]), fixtureChannel=str(artNetPatch["channel"]))

    if exportText:
    textElem = ET.SubElement(svg, "text", x=str((segStart[0]+segEnd[0])/2), y=str((segStart[1]+segEnd[1])/2), fill="red")
    textElem.text = str(segmentId)
    if y < gridSizeY-1: # Straight Down
    segStart = getPointPos(x,y)
    segStart[1] += exportPitch
    segEnd = getPointPos(x,y+2)
    segEndId = getPointId(x,y+2)
    segEnd[1] -= exportPitch
    segmentId = segStartId * 3 + 1;

    artNetPatch=getArtNetPatchForSegment(x,y,x,y+2)

    if artNetPatch["reversed"]:
    ET.SubElement(svg, "line", x2=str(segStart[0]), y2=str(segStart[1]), x1=str(segEnd[0]), y1=str(segEnd[1]), fill="none", stroke="#000000", fixtureDefinition="Generic - 60x1L", id="l"+str(segmentId), endPointId=str(segStartId), startPointId=str(segEndId), fixtureUniverse=str(artNetPatch["universe"]), fixtureChannel=str(artNetPatch["channel"]))
    else:
    ET.SubElement(svg, "line", x1=str(segStart[0]), y1=str(segStart[1]), x2=str(segEnd[0]), y2=str(segEnd[1]), fill="none", stroke="#000000", fixtureDefinition="Generic - 60x1L", id="l"+str(segmentId), startPointId=str(segStartId), endPointId=str(segEndId), fixtureUniverse=str(artNetPatch["universe"]), fixtureChannel=str(artNetPatch["channel"]))

    if exportText:
    textElem = ET.SubElement(svg, "text", x=str((segStart[0]+segEnd[0])/2), y=str((segStart[1]+segEnd[1])/2), fill="red")
    textElem.text = str(segmentId)
    if y < gridSizeY and x < gridSizeX: # Down right
    segStart = getPointPos(x,y)
    segStart[0] += exportPitch
    segStart[1] += exportPitch
    segEnd = getPointPos(x+1,y+1)
    segEndId = getPointId(x+1,y+1)
    segEnd[0] -= exportPitch
    segEnd[1] -= exportPitch
    segmentId = segStartId * 3 + 2;

    artNetPatch=getArtNetPatchForSegment(x,y,x+1,y+1)

    if artNetPatch["reversed"]:
    ET.SubElement(svg, "line", x2=str(segStart[0]), y2=str(segStart[1]), x1=str(segEnd[0]), y1=str(segEnd[1]), fill="none", stroke="#000000", fixtureDefinition="Generic - 60x1L", id="l"+str(segmentId), endPointId=str(segStartId), startPointId=str(segEndId), fixtureUniverse=str(artNetPatch["universe"]), fixtureChannel=str(artNetPatch["channel"]))
    else:
    ET.SubElement(svg, "line", x1=str(segStart[0]), y1=str(segStart[1]), x2=str(segEnd[0]), y2=str(segEnd[1]), fill="none", stroke="#000000", fixtureDefinition="Generic - 60x1L", id="l"+str(segmentId), startPointId=str(segStartId), endPointId=str(segEndId), fixtureUniverse=str(artNetPatch["universe"]), fixtureChannel=str(artNetPatch["channel"]))

    if exportText:
    textElem = ET.SubElement(svg, "text", x=str((segStart[0]+segEnd[0])/2), y=str((segStart[1]+segEnd[1])/2), fill="red")
    textElem.text = str(segmentId)

    segStart = getPointPos(x,y)

    if exportText:
    textElem = ET.SubElement(svg, "text", x=str(segStart[0]), y=str(segStart[1]+1), fill="green")
    textElem.text = str(segStartId)

    tree = ET.ElementTree(svg)

    tree.write(targetFile)

    # Create a 3D OBJ file for preview
    objFileContent = ""
    vIndices = {}
    vIdx = 1

    # Vertices
    for x in range(0,gridSizeX):
    vIndices[x]={}
    for y in range(0,gridSizeY+1):
    if x % 2 != y % 2:
    pos3D = getPoint3DPos(x,y)
    objFileContent += "v " + str(pos3D[0]) + " " + str(pos3D[1]) + " " + str(pos3D[2]) + "\n"
    vIndices[x][y] = vIdx
    vIdx += 1

    objFileContent += "\n"

    for x in range(0,gridSizeX+1):
    for y in range(0,gridSizeY+1):
    if x % 2 != y % 2:
    if y < gridSizeY and x > 0: # Down left
    objFileContent += "f " + str(vIndices[(x) % gridSizeX][y]) + " " + str(vIndices[(x-1) % gridSizeX][y+1]) + " " + str(vIndices[(x) % gridSizeX][y]) + "\n"
    pass

    if y < gridSizeY-1: # Straight Down
    objFileContent += "f " + str(vIndices[(x) % gridSizeX][y]) + " " + str(vIndices[(x) % gridSizeX][y+2]) + " " + str(vIndices[(x) % gridSizeX][y]) + "\n"
    pass

    if y < gridSizeY and x < gridSizeX: # Down right
    objFileContent += "f " + str(vIndices[(x) % gridSizeX][y]) + " " + str(vIndices[(x+1) % gridSizeX][y+1]) + " " + str(vIndices[(x) % gridSizeX][y]) + "\n"
    pass

    text_file = open("bars.obj", "w")
    text_file.write(objFileContent)
    text_file.close()
  • vnkbln
    junior Member
    Posts: 2
    Joined: Tue May 30, 2023 9:05 am

    Re: Import DMX Fixtures from CSV / SVG

    by vnkbln » Tue May 30, 2023 9:36 am

    Thank you for importing fixtures! This is a very handy feature for large projects!

    Can you please tell me how to set the fixture type when importing? I can import DMX fixtures, but not DMX lines (I need fixtures with arrows for directions)

    Image
  • mad-matt
    garageCube team
    Posts: 1475
    Joined: Mon Sep 09, 2013 5:50 pm

    Re: Import DMX Fixtures from CSV / SVG

    by mad-matt » Thu Jun 01, 2023 1:22 am

    Indeed, generally when importing from CSV/SVG we don't modify the mapping after, but if you need you might want a "Fixture Line". We should add this option.
  • vnkbln
    junior Member
    Posts: 2
    Joined: Tue May 30, 2023 9:05 am

    Re: Import DMX Fixtures from CSV / SVG

    by vnkbln » Fri Jun 02, 2023 8:47 am

    Fixture direction helps a lot with setup and troubleshooting, so it would be great if you add the ability to import "Fixture Line"
  • mad-matt
    garageCube team
    Posts: 1475
    Joined: Mon Sep 09, 2013 5:50 pm

    Re: Import DMX Fixtures from CSV / SVG

    by mad-matt » Mon Jun 19, 2023 10:34 am

    Okay that's in our whishlist.
  • mad-matt
    garageCube team
    Posts: 1475
    Joined: Mon Sep 09, 2013 5:50 pm

    Re: Import DMX Fixtures from CSV / SVG

    by mad-matt » Tue Sep 26, 2023 2:29 pm

    Another information, there's a option in SVG to specify the fixture line (as well as DMX universe/channel) width when importing from SVG, example:

    <svg height="717" version="1.1" width="1834">
    <line fill="none" id="Group 1/Fixture 1" stroke="#000000" x1="0" y1="0" x2="512" y2="512" fixtureDefinition="Generic - 60x1L" fixtureUniverse="2" fixtureChannel="1" fixtureWidthInPixels="5"/>
    <line fill="none" id="Group 1/Fixture 2" stroke="#000000" x1="0" y1="1024" x2="512" y2="512" fixtureDefinition="Generic - 60x1L" fixtureUniverse="2" fixtureChannel="61" fixtureWidthInPixels="5"/>
    <line fill="none" id="Group 2/Fixture 3" stroke="#000000" x1="1024" y1="0" x2="512" y2="512" fixtureDefinition="Generic - 60x1L" fixtureUniverse="3" fixtureChannel="1" fixtureWidthInPixels="50"/>
    <line fill="none" id="Group 2/Fixture 4" stroke="#000000" x1="1024" y1="1024" x2="512" y2="512" fixtureDefinition="Generic - 60x1L" fixtureUniverse="3" fixtureChannel="61" fixtureWidthInPixels="50"/>
    </svg>

Who is online

Users browsing this forum: No registered users and 9 guests