Skip to content

Instantly share code, notes, and snippets.

@seanwittmeyer
Last active December 15, 2019 19:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save seanwittmeyer/6e99bd96dfc6c3e1a4bc31e17d4830c1 to your computer and use it in GitHub Desktop.
Save seanwittmeyer/6e99bd96dfc6c3e1a4bc31e17d4830c1 to your computer and use it in GitHub Desktop.
grasshopper definition exporter for pylos
# Pylos: A Plugin for example file sharing
#
# This file is part of Pylos. It is an adaptation of the hydrashare platform by the Ladybug dev team (they rock!) for use in the zgf environment.
"""
Use this component to export your GH file to your Pylos repository so that you can upload and share with the community!
-
Provided by Pylos 1.1.2
Args:
_fileName: A text name for your example file.
fileTags_: An optional list of test tags to describe your example file. This will help others search for your file easily.
includeRhino_: Set to 'True' to include the Rhino file in the zip file that gets uploaded to your pylos fork. This is important if you are referencing Rhino geometry and have not internalized it in the GH document. By default, this is set to 'False', which will only include the grasshopper file.
GHForThumb_: Set to 'True' to have the thumbnail of your example file be of your GH canvas. By default, the thumbnail will be of the Rhino scene since this is where the cool geometric output of the file displays. If your file is not producing a geometric output, you will want to set this to 'True.'
_export: Set to "True" to export the Grasshopper file to Pylos.
_uploadtopylos: Set to "True" if you want the file uploaded to Pylos to share with others. Leave false if you only want to package up the file and save it in your Documents\Pylos folder.
Returns:
readMe!: ...
"""
ghenv.Component.Name = "Pylos_ExportFile"
ghenv.Component.NickName = 'exportPylos'
ghenv.Component.Message = 'ZGF/1.1.2/15DEC19'
ghenv.Component.Category = "Extra"
ghenv.Component.SubCategory = "Pylos"
try: ghenv.Component.AdditionalHelpFromDocStrings = "1"
except: pass
import Grasshopper.Kernel as gh
import scriptcontext as sc
import shutil
import os
import errno
import zipfile
import Grasshopper.GUI as ghGUI
import System
import Grasshopper.Instances as ghInst
import time
from time import mktime
from datetime import datetime
from datetime import date
import json
import Rhino as rc
import clr
import subprocess
from System.Diagnostics import Process
clr.AddReference("System.Xml")
from System.Xml import *
clr.AddReference("System.Web")
from System.Web import *
clr.AddReference("System.Net")
from System.Net import *
from System.Collections.Specialized import *
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12
pylosUrl = "https://pylos.zgf.com/pylos/api/uploadimage/wizard/grasshopper"
pylosFileFormName = "userfile"
contentType = "multipart/form-data"
#contentType = "application/octet-stream"
def checkTheInputs():
# Set a warning variable.
w = gh.GH_RuntimeMessageLevel.Warning
c = gh.GH_RuntimeMessageLevel.Remark
#Replace any spaces in the connected fileName with underscores.
fileName = _fileName.strip().replace(" ","_")
#Set a default directory to the pylos default.
checkData1 = True
repoTargetFolder = None
fullForkTarget = None
pylosFolder = None
username = os.getenv("USERNAME")
pylosDirectory = "C:\\Users\\" + username + "\\Documents\\Pylos\\"
#for file in os.listdir(pylosDirectory):
#if 'pylos' in file: pylosFolder = pylosDirectory + file + "\\"
if pylosFolder == None: pylosFolder = "C:\\Users\\" + username + "\\Documents\\Pylos\\"
if not os.path.isdir(pylosFolder):
os.makedirs(pylosFolder)
if pylosFolder.endswith('\\'):
repoTargetFolder = pylosFolder + fileName
fullForkTarget = pylosFolder
else:
repoTargetFolder = pylosFolder + '\\' + fileName
fullForkTarget = pylosFolder + '\\'
else:
repoTargetFolder = pylosFolder + fileName
fullForkTarget = pylosFolder
# Check the directory and create it if it does not exist. If it does exist, it is, there is not an old version of this example file and I should make a new directory.
gitUserTrigger = True
gitUserName = "grasshopper-pylos"
forkName = "pylos-" + fileName
checkData2 = True
#Make a list of file tags that includes the example file name if nothing else is connected.
fileTags = []
if not 'Grasshopper' in fileTags and \
not 'grasshopper' in fileTags:
fileTags.append('Grasshopper')
fileTags = [tag.strip() for tag in fileTags]
#Check to be sure that the user has saved this currently open GH file so that we can copy it.
checkData3 = True
document = ghenv.Component.OnPingDocument()
doucumentPath = document.FilePath
if doucumentPath!=None and os.path.isfile(doucumentPath): pass
else:
checkData3 = False
warning = "You have not yet saved this Grasshopper file to your machine. \n You must save it first in order to export it to Pylos."
print warning
ghenv.Component.AddRuntimeMessage(w, warning)
#Check to be sure that the user has saved this currently open Rhino file so that we can copy it.
checkData4 = True
rhinoDocPath = rc.RhinoDoc.ActiveDoc.Path
if not rhinoDocPath:
rhinoDocPath = ''
if os.path.isfile(rhinoDocPath): pass
elif not os.path.isfile(rhinoDocPath) and includeRhino_ == True:
checkData4 = False
warning = "You must save your Rhino file in order to export it to Pylos. \nEither save your file or set 'includeRhino_' to 'False'."
print warning
ghenv.Component.AddRuntimeMessage(w, warning)
else:
comment = "You have not yet saved this Rhino file to your machine. \nYou will be able to use the 'includeRhino_' input to this component to include the rhino file with your example."
print comment
ghenv.Component.AddRuntimeMessage(c, comment)
#Give a final check for everything.
if checkData1 == True and checkData2 == True and checkData3 == True and checkData4 == True: checkData = True
else: checkData = False
return checkData, fileName, fileTags, repoTargetFolder, doucumentPath, document, rhinoDocPath, gitUserName, forkName, fullForkTarget
def writeGHRhino(fileName, repoTargetFolder, doucumentPath, rhinoDocPath):
#Write out a list for the source files.
zipFileDirectory = repoTargetFolder + '\\' + fileName
if not os.path.isdir(zipFileDirectory): os.mkdir(zipFileDirectory)
#Write out a GH file to the folder.
ghFilePath = zipFileDirectory + '\\' + fileName + '.gh'
shutil.copyfile(doucumentPath, ghFilePath)
#If the user has requested writing the Rhino file, write it there.
rhinoWritten = False
rhioDestinationPath = zipFileDirectory + '\\' + fileName + '.3dm'
if includeRhino_:
if rhinoDocPath != '':
shutil.copyfile(rhinoDocPath, rhioDestinationPath)
rhinoWritten = True
else:
rhioDestinationPath = zipFileDirectory + '\\' + fileName + '.3dm'
if os.path.isfile(rhioDestinationPath):
os.remove(rhioDestinationPath)
#Zip the files together.
zipFilePath = repoTargetFolder + '\\' + fileName
zf = zipfile.ZipFile("%s.zip" % (zipFilePath), "w", zipfile.ZIP_DEFLATED)
abs_src = os.path.abspath(zipFileDirectory)
for dirname, subdirs, files in os.walk(zipFileDirectory):
for filename in files:
absname = os.path.abspath(os.path.join(dirname, filename))
arcname = absname[len(abs_src) + 1:]
zf.write(absname, arcname)
zf.close()
#Delete the old zip directory.
shutil.rmtree(zipFileDirectory)
return zipFilePath
def writeReadMe(fileName, repoTargetFolder, gitUserName, fileTags, forkName):
readMeFilePath = repoTargetFolder + '\\' + 'README.md'
#Write in the file tags.
readMeFile = open(readMeFilePath, "w")
readMeFile.write('### Tags \n')
for linecount, line in enumerate(fileTags):
if linecount+1 != len(fileTags): readMeFile.write(line + ', ')
else: readMeFile.write(line)
readMeFile.write('\n')
#Write in a link to the thumbnail.
readMeFile.write('### Thumbnail \n')
readMeFile.write('\n')
readMeFile.close()
return readMeFilePath
def getAllTheComponents(document, onlyGHPython = False):
components = []
for component in document.Objects:
if onlyGHPython and type(component)!= type(ghenv.Component): pass
else: components.append(component)
return components
def getMetaData(ghComps, fileTags, fileName, rhinoDocPath):
#Create the dictionary.
metaDataDict = {}
#Get the names of all of the components on the canvas and add them to the dictionary.
metaDataDict["components"] = {}
metaDataDict["images"] = []
metaDataDict["videos"] = {}
metaDataDict["dependencies"] = []
metaDataDict["filename"] = fileName
grasshopperNative = ['Params', 'Maths', 'Sets', 'Vector', 'Curve', 'Surface', 'Mesh', 'Intersect', 'Transform', 'Display', 'Pylos']
notImportantComps = ['Pylos', 'Pylos_ExportFile', 'Pylos_ImportFile', 'Group', 'Slider', 'Boolean Toggle', 'Custom Preview', 'Colour Swatch', 'Button', 'Control Knob', 'Digit Scroller', 'MD Slider', 'Value List', 'Point', 'Vector', 'Circle', 'Circular Arc', 'Curve', 'Line', 'Plane', 'Mesh', 'Mesh Face', 'Surface', 'Twisted Box', 'Field', 'Geometry', 'Geometry Cache', 'Transform']
for component in ghComps:
componentName = component.Name
if componentName in notImportantComps: pass
elif metaDataDict["components"].has_key(componentName):
metaDataDict["components"][componentName] += 1
else:
metaDataDict["components"][componentName] = 1
# find dependencies
componentCategory = component.Category
if componentCategory == "Extra" or componentCategory == "User":
componentCategory = component.SubCategory
if componentCategory not in grasshopperNative + metaDataDict["dependencies"]:
metaDataDict["dependencies"].append(componentCategory)
#Put in tags.
metaDataDict['tags'] = fileTags
# Put in the path to the gh scene image.
metaDataDict["images"].append({fileName + '_GH.jpg' : "Grasshopper Definition"})
# Put in the path to the rhino scene image.
if not GHForThumb_:
metaDataDict["images"].append({fileName + '_Rhino.jpg' : "Rhino Viewport Screenshot"})
# Put in the path to the thumbnail file.
metaDataDict['thumbnail'] = 'thumbnail.jpg'
# Put in the path to the zip file.
metaDataDict['file'] = fileName + '.zip'
return metaDataDict
def writeMetadataFile(fileName, repoTargetFolder, metaDataDict):
jsonFilePath = repoTargetFolder + '\\' + 'input.json'
with open(jsonFilePath, "w") as outfile:
json.dump(metaDataDict, outfile)
return jsonFilePath
def writeGHImage(fileName, repoTargetFolder):
#Set the final File Path.
filePathName = repoTargetFolder + '\\' + fileName + '_GH.jpg'
#Take a tiled image using the GH Method.
imageSettings = ghGUI.Canvas.GH_Canvas.GH_ImageSettings()
canvas = ghInst.ActiveCanvas
rect = gh.GH_Convert.ToRectangle(canvas.Document.BoundingBox(False))
#System.Drawing.Rectangle
imgsOfCanvas = ghGUI.Canvas.GH_Canvas.GenerateHiResImage(canvas, rect, imageSettings)
# http://www.grasshopper3d.com/forum/topics/access-to-grasshopper-image-stitcher-ghis
#Prepare a large image to absorb the smaller images.
tileWidth = 1000
tileHeight = 1000
width = int(str(imgsOfCanvas[-1]).split(',')[0].split('=')[-1])
height = int(str(imgsOfCanvas[-1]).split(',')[-1].split('=')[-1].split('}')[0])
smallImages = imgsOfCanvas[0]
#If the image is too large, reszie it.
if width > 16000 : width = 16000
if height > 16000: height = 16000
#Build the big image.
fullImage = System.Drawing.Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format24bppRgb)
fullFrame = System.Drawing.Rectangle(0, 0, tileWidth, tileHeight)
fullGraphics = System.Drawing.Graphics.FromImage(fullImage)
fullGraphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor
fullGraphics.Clear(System.Drawing.Color.White)
#Re-order the list of images to be correct
newSmallImgDict = {}
for image in smallImages:
imageEnd = str(image).split('\\')[-1].split('.png')[0]
imageHoriz = imageEnd.split(';')[0]
imageVert = imageEnd.split(';')[-1]
if not newSmallImgDict.has_key(imageHoriz) and int(imageHoriz) <= 15 and int(imageVert) <= 15: newSmallImgDict[imageHoriz] = [image]
elif int(imageHoriz) <= 15 and int(imageVert) <= 15: newSmallImgDict[imageHoriz].append(image)
#Join the smaller images into the larger images.
fullFrame.Y = 0
for columnNum in range(len(newSmallImgDict)):
for rowNum in range(len(newSmallImgDict['0'])):
#Select out the correct image.
image = newSmallImgDict[str(columnNum)][rowNum]
#Adjust the frame.
#We need to move it right by tileWidth pixel
#If the frame goes past the right, move it to the top of the next column.
if fullFrame.Y >= height:
fullFrame.Y = 0
fullFrame.X += tileWidth
#Now load the target image from the disk...
targetImage = System.Drawing.Bitmap(image)
#...and paint it into the target frame
fullGraphics.DrawImage(targetImage, fullFrame)
#Delete the targetImage from memory.
targetImage.Dispose()
fullFrame.Y += tileHeight
#Save the image and dispose of it from memory.
fullImage.Save(filePathName , System.Drawing.Imaging.ImageFormat.Jpeg)
fullGraphics.Dispose()
return filePathName, fullImage, width, height
def writeRhinoImage(fileName, repoTargetFolder):
viewtoCapture = sc.doc.Views.ActiveView
dispMode = viewtoCapture.ActiveViewport.DisplayMode
image_h = viewtoCapture.ActiveViewport.Size.Height
image_w = viewtoCapture.ActiveViewport.Size.Width
viewSize = System.Drawing.Size(int(image_w), int(image_h))
pic = rc.Display.RhinoView.CaptureToBitmap(viewtoCapture , viewSize)
fullPath = repoTargetFolder + '\\' + fileName + '_Rhino.jpg'
System.Drawing.Bitmap.Save(pic , fullPath , System.Drawing.Imaging.ImageFormat.Jpeg)
return fullPath, pic, image_w, image_h
def makeThumbnailImg(ghImgFile, rhinoImgFile, gw, gh, rw, rh, repoTargetFolder):
#Set universal variables.
thumbnailPath = repoTargetFolder + '\\' + 'thumbnail.jpg'
thumbnailW = 500
if GHForThumb_:
#Calculate what the height should be.
imgRatio = gh/gw
thumbnailH = thumbnailW*imgRatio
imgSize = System.Drawing.Size(thumbnailW, thumbnailH)
imgHeight = ghImgFile.Height
#Get the thumbnail image and save it
fullImage = System.Drawing.Bitmap(ghImgFile,imgSize)
System.Drawing.Bitmap.Save(fullImage , thumbnailPath , System.Drawing.Imaging.ImageFormat.Jpeg)
else:
#Calculate what the height should be.
imgRatio = rh/rw
thumbnailH = thumbnailW*imgRatio
imgSize = System.Drawing.Size(thumbnailW, thumbnailH)
#Get the thumbnail image and save it
fullImage = System.Drawing.Bitmap(rhinoImgFile,imgSize)
System.Drawing.Bitmap.Save(fullImage , thumbnailPath , System.Drawing.Imaging.ImageFormat.Jpeg)
return thumbnailPath, fullImage
def main(fileName, fileTags, repoTargetFolder, doucumentPath, \
document, rhinoDocPath, gitUserName, forkName, fullForkTarget):
#Write the GH file into a GHX file.
ghFile = writeGHRhino(fileName, repoTargetFolder, doucumentPath, rhinoDocPath)
#Take a high-res image of the grasshopper canvass.
ghImgFile, ghImg, gw, gh = writeGHImage(fileName, repoTargetFolder)
#Take an image of the Rhino scene
if not GHForThumb_:
rhinoImgFile, rhImg, rw, rh = writeRhinoImage(fileName, repoTargetFolder)
else:
rhImg, rw, rh = "", 1, 1
#Make a thumbnail image.
thumbnailImgFile, thumbnailImg = makeThumbnailImg(ghImg, rhImg, gw, gh, rw, rh, repoTargetFolder)
#Clear the images from the computer memory.
ghImg.Dispose()
if not GHForThumb_:
rhImg.Dispose()
thumbnailImg.Dispose()
#Write the readMe into a text file.
descriptFile = writeReadMe(fileName, repoTargetFolder, gitUserName, fileTags, forkName)
#Get all of the components on the canvas and pull out their information so that they can be written into a metadata file.
ghComps = getAllTheComponents(document)
metaDataDict = getMetaData(ghComps, fileTags, fileName, rhinoDocPath)
metadataFile = writeMetadataFile(fileName, repoTargetFolder, metaDataDict)
# zip it all up
zipTimeStamp = str(date.today()) + "_" + fileName
#Write out a list for the source files.
zipFileDirectory = repoTargetFolder
if not os.path.isdir(zipFileDirectory):
msg = "Hmmm... zipping up full file, ran into an issue "
print msg
#Zip the files together.
zipFilePath = fullForkTarget + zipTimeStamp
zf = zipfile.ZipFile("%s.zip" % (zipFilePath), "w", zipfile.ZIP_DEFLATED)
abs_src = os.path.abspath(zipFileDirectory)
for dirname, subdirs, files in os.walk(zipFileDirectory):
for filename in files:
absname = os.path.abspath(os.path.join(dirname, filename))
arcname = absname[len(abs_src) + 1:]
zf.write(absname, arcname)
zf.close()
#Delete the old zip directory.
shutil.rmtree(zipFileDirectory)
print "We packaged your pylos block here:"
print zipFilePath + ".zip"
return zipFilePath + ".zip"
def upload(upload_file,url,file_form_name,content_type,query_string):
try:
if file_form_name == None or file_form_name.Length == 0:
file_form_name = 'file'
if content_type == None or content_type.Length == 0:
content_type = 'application/octet-stream'
post_data = '?'
if query_string != None:
for key in query_string.Keys:
post_data += key + '=' + query_string.Get(key) + '&'
uri = System.Uri(url + post_data)
#print "sending the model to " + str(uri)
#print "preparing file for upload..."
boundary = '----------' + System.DateTime.Now.Ticks.ToString('x')
web_request = WebRequest.Create(uri)
web_request.ContentType = 'multipart/form-data; boundary=' + \
boundary
web_request.Method = 'POST'
sb = System.Text.StringBuilder()
sb.Append('--')
sb.Append(boundary)
sb.Append('\r\n')
sb.Append('Content-Disposition: form-data; name="')
sb.Append(file_form_name)
sb.Append('"; filename="')
sb.Append(System.IO.Path.GetFileName(upload_file))
sb.Append('"')
sb.Append('\r\n')
sb.Append('Content-Type: ')
sb.Append(content_type)
sb.Append('\r\n')
sb.Append('\r\n')
post_header = sb.ToString()
post_header_bytes = System.Text.Encoding.UTF8.GetBytes(post_header)
boundary_bytes = System.Text.Encoding.ASCII.GetBytes('\r\n--' + boundary + \
'\r\n')
print "preparing and uploading, may take a moment depending on the file sizes..."
file_stream = System.IO.FileStream(upload_file,System.IO.FileMode.Open,System.IO.FileAccess.Read)
length = post_header_bytes.Length + file_stream.Length + \
boundary_bytes.Length
web_request.ContentLength = length
request_stream = web_request.GetRequestStream()
request_stream.Write(post_header_bytes,0,post_header_bytes.Length)
file_bytes = System.Text.Encoding.UTF8.GetBytes(file_stream.ToString())
while True:
bytes_read = file_stream.Read(file_bytes, 0, file_bytes.Length)
request_stream.Write(file_bytes, 0, bytes_read)
if bytes_read == 0:
break
#print "success"
request_stream.Write(boundary_bytes,0,boundary_bytes.Length)
response = web_request.GetResponse()
s = response.GetResponseStream()
sr = System.IO.StreamReader(s)
importUrl = sr.ReadToEnd()
if importUrl and (importUrl.startswith('https://') or importUrl.startswith('http://')):
print "finish importing your block into pylos by visiting this website:"
print importUrl
Process.Start(importUrl)
response.Close()
s.Close()
sr.Close()
file_stream.Close()
return importUrl
except Exception,e:
print e
return e
if _export:
if _fileName != 0:
checkData, fileName, fileTags, repoTargetFolder, \
doucumentPath, document, rhinoDocPath, gitUserName, forkName, fullForkTarget = checkTheInputs()
if checkData == True:
zipFilePath = main(fileName, fileTags, repoTargetFolder, \
doucumentPath, document, rhinoDocPath, gitUserName, \
forkName, fullForkTarget)
queryString = None
print zipFilePath
if _uploadtopylos == True:
upload(zipFilePath, pylosUrl, pylosFileFormName, contentType, queryString)
else:
subprocess.Popen(r'explorer /select,"'+zipFilePath+'"')
else:
msg = "Aww snap! One of the mandatory inputs is missing!"
print msg
ghenv.Component.AddRuntimeMessage(gh.GH_RuntimeMessageLevel.Warning, msg)
@seanwittmeyer
Copy link
Author

Issue: Images output from this grasshopper component require too much memory on the pylos platform to convert and manipulate, need to use this to make thumbs of all images.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment