Last active
December 15, 2019 19:36
-
-
Save seanwittmeyer/6e99bd96dfc6c3e1a4bc31e17d4830c1 to your computer and use it in GitHub Desktop.
grasshopper definition exporter for pylos
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.