Files
geMoldInsight/venv/lib/python3.11/site-packages/vtkmodules/web/render_window_serializer.py
T
2026-02-12 23:27:48 +08:00

1411 lines
49 KiB
Python

import io
import logging
import struct
import time
import zipfile
from vtkmodules.web import (
base64Encode,
hashDataArray,
getJSArrayType,
arrayTypesMapping,
getReferenceId,
)
from vtkmodules.vtkCommonCore import vtkTypeUInt32Array
from vtkmodules.vtkFiltersGeometry import vtkCompositeDataGeometryFilter
from vtkmodules.vtkFiltersGeometry import vtkDataSetSurfaceFilter
from vtkmodules.vtkRenderingCore import vtkColorTransferFunction
logger = logging.getLogger(__name__)
# Always DEBUG level for this logger. Users can change this
logger.setLevel(logging.DEBUG)
# -----------------------------------------------------------------------------
# Array helpers
# -----------------------------------------------------------------------------
def zipCompression(name, data):
with io.BytesIO() as in_memory:
with zipfile.ZipFile(in_memory, mode="w") as zf:
zf.writestr("data/%s" % name, data, zipfile.ZIP_DEFLATED)
in_memory.seek(0)
return in_memory.read()
def dataTableToList(dataTable):
dataType = arrayTypesMapping[dataTable.GetDataType()]
elementSize = struct.calcsize(dataType)
nbValues = dataTable.GetNumberOfValues()
nbComponents = dataTable.GetNumberOfComponents()
nbytes = elementSize * nbValues
if dataType != " ":
with io.BytesIO(memoryview(dataTable)) as stream:
data = list(struct.unpack(dataType * nbValues, stream.read(nbytes)))
return [
data[idx * nbComponents : (idx + 1) * nbComponents]
for idx in range(nbValues // nbComponents)
]
return None
# -----------------------------------------------------------------------------
def linspace(start, stop, num):
delta = (stop - start) / (num - 1)
return [start + i * delta for i in range(num)]
# -----------------------------------------------------------------------------
# Convenience class for caching data arrays, storing computed sha sums, keeping
# track of valid actors, etc...
# -----------------------------------------------------------------------------
class SynchronizationContext:
def __init__(self):
self.dataArrayCache = {}
self.lastDependenciesMapping = {}
self.ingoreLastDependencies = False
def setIgnoreLastDependencies(self, force):
self.ingoreLastDependencies = force
def cacheDataArray(self, pMd5, data):
self.dataArrayCache[pMd5] = data
def getCachedDataArray(self, pMd5, binary=False, compression=False):
cacheObj = self.dataArrayCache[pMd5]
array = cacheObj["array"]
cacheTime = cacheObj["mTime"]
if cacheTime != array.GetMTime():
logger.debug(" ***** ERROR: you asked for an old cache key! ***** ")
if array.GetDataType() == 12:
# IdType need to be converted to Uint32
arraySize = array.GetNumberOfTuples() * array.GetNumberOfComponents()
newArray = vtkTypeUInt32Array()
newArray.SetNumberOfTuples(arraySize)
for i in range(arraySize):
newArray.SetValue(i, -1 if array.GetValue(i) < 0 else array.GetValue(i))
pBuffer = memoryview(newArray)
else:
pBuffer = memoryview(array)
if binary:
# Convert the vtkUnsignedCharArray into a bytes object, required by
# Autobahn websockets
return (
pBuffer.tobytes()
if not compression
else zipCompression(pMd5, pBuffer.tobytes())
)
return base64Encode(
pBuffer if not compression else zipCompression(pMd5, pBuffer.tobytes())
)
def checkForArraysToRelease(self, timeWindow=20):
cutOffTime = time.time() - timeWindow
shasToDelete = []
for sha in self.dataArrayCache:
record = self.dataArrayCache[sha]
array = record["array"]
count = array.GetReferenceCount()
if count == 1 and record["ts"] < cutOffTime:
shasToDelete.append(sha)
for sha in shasToDelete:
del self.dataArrayCache[sha]
def getLastDependencyList(self, idstr):
lastDeps = []
if idstr in self.lastDependenciesMapping and not self.ingoreLastDependencies:
lastDeps = self.lastDependenciesMapping[idstr]
return lastDeps
def setNewDependencyList(self, idstr, depList):
self.lastDependenciesMapping[idstr] = depList
def buildDependencyCallList(self, idstr, newList, addMethod, removeMethod):
oldList = self.getLastDependencyList(idstr)
calls = []
calls += [[addMethod, [wrapId(x)]] for x in newList if x not in oldList]
calls += [[removeMethod, [wrapId(x)]] for x in oldList if x not in newList]
self.setNewDependencyList(idstr, newList)
return calls
# -----------------------------------------------------------------------------
# Global variables
# -----------------------------------------------------------------------------
SERIALIZERS = {}
JS_CLASS_MAPPING = {}
context = None
# -----------------------------------------------------------------------------
# Global API
# -----------------------------------------------------------------------------
def registerInstanceSerializer(name, method):
global SERIALIZERS
SERIALIZERS[name] = method
def registerJSClass(vtk_class, js_class):
global JS_CLASS_MAPPING
JS_CLASS_MAPPING[vtk_class] = js_class
def class_name(vtk_obj):
vtk_class = vtk_obj.GetClassName()
if vtk_class in JS_CLASS_MAPPING:
return JS_CLASS_MAPPING[vtk_class]
return vtk_class
# -----------------------------------------------------------------------------
def serializeInstance(parent, instance, instanceId, context, depth):
instanceType = class_name(instance)
serializer = SERIALIZERS[instanceType] if instanceType in SERIALIZERS else None
if serializer:
return serializer(parent, instance, instanceId, context, depth)
logger.error(f"!!!No serializer for {instanceType} with id {instanceId}")
return None
# -----------------------------------------------------------------------------
def initializeSerializers():
# Actors/viewProps
registerInstanceSerializer("vtkActor", genericActorSerializer)
registerInstanceSerializer("vtkOpenGLActor", genericActorSerializer)
registerInstanceSerializer("vtkPVLODActor", genericActorSerializer)
# Volume/viewProps
registerInstanceSerializer("vtkVolume", genericVolumeSerializer)
# Mappers
registerInstanceSerializer("vtkMapper", genericMapperSerializer)
registerInstanceSerializer("vtkDataSetMapper", genericMapperSerializer)
registerInstanceSerializer("vtkPolyDataMapper", genericMapperSerializer)
registerInstanceSerializer("vtkImageDataMapper", genericMapperSerializer)
registerInstanceSerializer("vtkOpenGLPolyDataMapper", genericMapperSerializer)
registerInstanceSerializer("vtkCompositePolyDataMapper2", genericMapperSerializer)
registerJSClass("vtkPolyDataMapper", "vtkMapper")
registerJSClass("vtkDataSetMapper", "vtkMapper")
registerJSClass("vtkOpenGLPolyDataMapper", "vtkMapper")
registerJSClass("vtkCompositePolyDataMapper2", "vtkMapper")
registerInstanceSerializer("vtkVolumeMapper", genericVolumeMapperSerializer)
registerInstanceSerializer("vtkFixedPointVolumeRayCastMapper", genericVolumeMapperSerializer)
registerJSClass("vtkFixedPointVolumeRayCastMapper", "vtkVolumeMapper")
# LookupTables/TransferFunctions
registerInstanceSerializer("vtkLookupTable", lookupTableSerializer2)
registerInstanceSerializer(
"vtkPVDiscretizableColorTransferFunction", discretizableColorTransferFunctionSerializer
)
registerInstanceSerializer(
"vtkColorTransferFunction", colorTransferFunctionSerializer
)
registerInstanceSerializer("vtkPiecewiseFunction", pwfSerializer)
# Textures
registerInstanceSerializer("vtkTexture", textureSerializer)
registerInstanceSerializer("vtkOpenGLTexture", textureSerializer)
# Property
registerInstanceSerializer("vtkProperty", propertySerializer)
registerInstanceSerializer("vtkOpenGLProperty", propertySerializer)
# VolumeProperty
registerInstanceSerializer("vtkVolumeProperty", volumePropertySerializer)
# Datasets
registerInstanceSerializer("vtkPolyData", polydataSerializer)
registerInstanceSerializer("vtkImageData", imagedataSerializer)
registerInstanceSerializer("vtkUnstructuredGrid", mergeToPolydataSerializer)
registerInstanceSerializer("vtkMultiBlockDataSet", mergeToPolydataSerializer)
registerInstanceSerializer("vtkStructuredPoints", imagedataSerializer)
registerJSClass("vtkStructuredPoints", "vtkImageData")
# RenderWindows
registerInstanceSerializer("vtkRenderWindow", renderWindowSerializer)
registerInstanceSerializer("vtkCocoaRenderWindow", renderWindowSerializer)
registerInstanceSerializer("vtkXOpenGLRenderWindow", renderWindowSerializer)
registerInstanceSerializer("vtkWin32OpenGLRenderWindow", renderWindowSerializer)
registerInstanceSerializer("vtkEGLRenderWindow", renderWindowSerializer)
registerInstanceSerializer("vtkOpenVRRenderWindow", renderWindowSerializer)
registerInstanceSerializer("vtkOpenXRRenderWindow", renderWindowSerializer)
registerInstanceSerializer("vtkGenericOpenGLRenderWindow", renderWindowSerializer)
registerInstanceSerializer("vtkOSOpenGLRenderWindow", renderWindowSerializer)
registerInstanceSerializer("vtkOpenGLRenderWindow", renderWindowSerializer)
registerInstanceSerializer("vtkIOSRenderWindow", renderWindowSerializer)
registerInstanceSerializer("vtkExternalOpenGLRenderWindow", renderWindowSerializer)
registerInstanceSerializer("vtkOffscreenOpenGLRenderWindow", renderWindowSerializer)
# Renderers
registerInstanceSerializer("vtkRenderer", rendererSerializer)
registerInstanceSerializer("vtkOpenGLRenderer", rendererSerializer)
# Cameras
registerInstanceSerializer("vtkCamera", cameraSerializer)
registerInstanceSerializer("vtkOpenGLCamera", cameraSerializer)
# Lights
registerInstanceSerializer("vtkLight", lightSerializer)
registerInstanceSerializer("vtkPVLight", lightSerializer)
registerInstanceSerializer("vtkOpenGLLight", lightSerializer)
# Annotations (ScalarBar/CubeAxes
registerInstanceSerializer("vtkCubeAxesActor", cubeAxesSerializer)
registerInstanceSerializer("vtkScalarBarActor", scalarBarActorSerializer)
# -----------------------------------------------------------------------------
# Helper functions
# -----------------------------------------------------------------------------
def pad(depth):
padding = ""
for _ in range(depth):
padding += " "
return padding
# -----------------------------------------------------------------------------
def wrapId(idStr):
return "instance:${%s}" % idStr
# -----------------------------------------------------------------------------
dataArrayShaMapping = {}
def digest(array):
objId = getReferenceId(array)
record = None
if objId in dataArrayShaMapping:
record = dataArrayShaMapping[objId]
if record and record["mtime"] == array.GetMTime():
return record["sha"]
record = {"sha": hashDataArray(array), "mtime": array.GetMTime()}
dataArrayShaMapping[objId] = record
return record["sha"]
# -----------------------------------------------------------------------------
def getRangeInfo(array, component):
r = array.GetRange(component)
compRange = {}
compRange["min"] = r[0]
compRange["max"] = r[1]
compRange["component"] = array.GetComponentName(component)
return compRange
# -----------------------------------------------------------------------------
def getArrayDescription(array, context):
if not array:
return None
pMd5 = digest(array)
context.cacheDataArray(
pMd5, {"array": array, "mTime": array.GetMTime(), "ts": time.time()}
)
root = {}
root["hash"] = pMd5
root["vtkClass"] = "vtkDataArray"
root["name"] = array.GetName()
root["dataType"] = getJSArrayType(array)
root["numberOfComponents"] = array.GetNumberOfComponents()
root["size"] = array.GetNumberOfComponents() * array.GetNumberOfTuples()
root["ranges"] = []
if root["numberOfComponents"] > 1:
for i in range(root["numberOfComponents"]):
root["ranges"].append(getRangeInfo(array, i))
root["ranges"].append(getRangeInfo(array, -1))
else:
root["ranges"].append(getRangeInfo(array, 0))
return root
# -----------------------------------------------------------------------------
def extractRequiredFields(
extractedFields, parent, dataset, context, requestedFields=["Normals", "TCoords"]
):
arrays_to_export = set()
export_all = "*" in requestedFields
# Identify arrays to export
if not export_all:
# FIXME should evolve and support funky mapper which leverage many arrays
if parent and parent.IsA("vtkMapper"):
mapper = parent
scalarVisibility = mapper.GetScalarVisibility()
arrayAccessMode = mapper.GetArrayAccessMode()
colorArrayName = (
mapper.GetArrayName() if arrayAccessMode == 1 else mapper.GetArrayId()
)
# colorMode = mapper.GetColorMode()
scalarMode = mapper.GetScalarMode()
if scalarVisibility and scalarMode in (1, 3):
array_to_export = dataset.GetPointData().GetArray(colorArrayName)
if array_to_export is None:
array_to_export = dataset.GetPointData().GetScalars()
arrays_to_export.add(array_to_export)
if scalarVisibility and scalarMode in (2, 4):
array_to_export = dataset.GetCellData().GetArray(colorArrayName)
if array_to_export is None:
array_to_export = dataset.GetCellData().GetScalars()
arrays_to_export.add(array_to_export)
if scalarVisibility and scalarMode == 0:
array_to_export = dataset.GetPointData().GetScalars()
if array_to_export is None:
array_to_export = dataset.GetCellData().GetScalars()
arrays_to_export.add(array_to_export)
if parent and parent.IsA("vtkTexture") and dataset.GetPointData().GetScalars():
arrays_to_export.add(dataset.GetPointData().GetScalars())
arrays_to_export.update(
[
getattr(dataset.GetPointData(), "Get" + requestedField, lambda: None)()
for requestedField in requestedFields
]
)
# Browse all arrays
for location, field_data in [
("pointData", dataset.GetPointData()),
("cellData", dataset.GetCellData()),
]:
for array_index in range(field_data.GetNumberOfArrays()):
array = field_data.GetArray(array_index)
if export_all or array in arrays_to_export:
arrayMeta = getArrayDescription(array, context)
if arrayMeta:
arrayMeta["location"] = location
attribute = field_data.IsArrayAnAttribute(array_index)
arrayMeta["registration"] = (
"set" + field_data.GetAttributeTypeAsString(attribute)
if attribute >= 0
else "addArray"
)
extractedFields.append(arrayMeta)
# -----------------------------------------------------------------------------
# Concrete instance serializers
# -----------------------------------------------------------------------------
def genericActorSerializer(parent, actor, actorId, context, depth):
# This kind of actor has two "children" of interest, a property and a
# mapper
actorVisibility = actor.GetVisibility()
mapperInstance = None
propertyInstance = None
calls = []
dependencies = []
if actorVisibility:
mapper = None
if not hasattr(actor, "GetMapper"):
logger.debug("This actor does not have a GetMapper method")
else:
mapper = actor.GetMapper()
if mapper:
mapperId = getReferenceId(mapper)
mapperInstance = serializeInstance(
actor, mapper, mapperId, context, depth + 1
)
if mapperInstance:
dependencies.append(mapperInstance)
calls.append(["setMapper", [wrapId(mapperId)]])
prop = None
if hasattr(actor, "GetProperty"):
prop = actor.GetProperty()
else:
logger.debug("This actor does not have a GetProperty method")
if prop:
propId = getReferenceId(prop)
propertyInstance = serializeInstance(
actor, prop, propId, context, depth + 1
)
if propertyInstance:
dependencies.append(propertyInstance)
calls.append(["setProperty", [wrapId(propId)]])
# Handle texture if any
texture = None
if hasattr(actor, "GetTexture"):
texture = actor.GetTexture()
else:
logger.debug("This actor does not have a GetTexture method")
if texture:
textureId = getReferenceId(texture)
textureInstance = serializeInstance(
actor, texture, textureId, context, depth + 1
)
if textureInstance:
dependencies.append(textureInstance)
calls.append(["addTexture", [wrapId(textureId)]])
if actorVisibility == 0 or (mapperInstance and propertyInstance):
return {
"parent": getReferenceId(parent),
"id": actorId,
"type": class_name(actor),
"properties": {
# vtkProp
"visibility": actorVisibility,
"pickable": actor.GetPickable(),
"dragable": actor.GetDragable(),
"useBounds": actor.GetUseBounds(),
# vtkProp3D
"origin": actor.GetOrigin(),
"position": actor.GetPosition(),
"scale": actor.GetScale(),
# vtkActor
"forceOpaque": actor.GetForceOpaque(),
"forceTranslucent": actor.GetForceTranslucent(),
},
"calls": calls,
"dependencies": dependencies,
}
return None
# -----------------------------------------------------------------------------
def genericVolumeSerializer(parent, actor, actorId, context, depth):
# This kind of actor has two "children" of interest, a property and a
# mapper
actorVisibility = actor.GetVisibility()
mapperInstance = None
propertyInstance = None
calls = []
dependencies = []
if actorVisibility:
mapper = None
if not hasattr(actor, "GetMapper"):
logger.debug("This actor does not have a GetMapper method")
else:
mapper = actor.GetMapper()
if mapper:
mapperId = getReferenceId(mapper)
mapperInstance = serializeInstance(
actor, mapper, mapperId, context, depth + 1
)
if mapperInstance:
dependencies.append(mapperInstance)
calls.append(["setMapper", [wrapId(mapperId)]])
prop = None
if hasattr(actor, "GetProperty"):
prop = actor.GetProperty()
else:
logger.debug("This actor does not have a GetProperty method")
if prop:
propId = getReferenceId(prop)
propertyInstance = serializeInstance(
actor, prop, propId, context, depth + 1
)
if propertyInstance:
dependencies.append(propertyInstance)
calls.append(["setProperty", [wrapId(propId)]])
if actorVisibility == 0 or (mapperInstance and propertyInstance):
return {
"parent": getReferenceId(parent),
"id": actorId,
"type": class_name(actor),
"properties": {
# vtkProp
"visibility": actorVisibility,
"pickable": actor.GetPickable(),
"dragable": actor.GetDragable(),
"useBounds": actor.GetUseBounds(),
# vtkProp3D
"origin": actor.GetOrigin(),
"position": actor.GetPosition(),
"scale": actor.GetScale(),
},
"calls": calls,
"dependencies": dependencies,
}
return None
# -----------------------------------------------------------------------------
def textureSerializer(parent, texture, textureId, context, depth):
# This kind of mapper requires us to get 2 items: input data and lookup
# table
dataObject = None
dataObjectInstance = None
calls = []
dependencies = []
if hasattr(texture, "GetInput"):
dataObject = texture.GetInput()
else:
logger.debug("This texture does not have GetInput method")
if dataObject:
dataObjectId = "%s-texture" % textureId
dataObjectInstance = serializeInstance(
texture, dataObject, dataObjectId, context, depth + 1
)
if dataObjectInstance:
dependencies.append(dataObjectInstance)
calls.append(["setInputData", [wrapId(dataObjectId)]])
if dataObjectInstance:
return {
"parent": getReferenceId(parent),
"id": textureId,
"type": "vtkTexture",
"properties": {
"interpolate": texture.GetInterpolate(),
"repeat": texture.GetRepeat(),
"edgeClamp": texture.GetEdgeClamp(),
},
"calls": calls,
"dependencies": dependencies,
}
return None
# -----------------------------------------------------------------------------
def genericMapperSerializer(parent, mapper, mapperId, context, depth):
# This kind of mapper requires us to get 2 items: input data and lookup
# table
dataObject = None
dataObjectInstance = None
lookupTableInstance = None
calls = []
dependencies = []
if hasattr(mapper, "GetInputDataObject"):
mapper.GetInputAlgorithm().Update()
dataObject = mapper.GetInputDataObject(0, 0)
else:
logger.debug("This mapper does not have GetInputDataObject method")
if dataObject:
if dataObject.IsA("vtkDataSet"):
alg = vtkDataSetSurfaceFilter()
alg.SetInputData(dataObject)
alg.Update()
dataObject = alg.GetOutput()
dataObjectId = "%s-dataset" % mapperId
dataObjectInstance = serializeInstance(
mapper, dataObject, dataObjectId, context, depth + 1
)
if dataObjectInstance:
dependencies.append(dataObjectInstance)
calls.append(["setInputData", [wrapId(dataObjectId)]])
lookupTable = None
if hasattr(mapper, "GetLookupTable"):
lookupTable = mapper.GetLookupTable()
else:
logger.debug("This mapper does not have GetLookupTable method")
if lookupTable:
lookupTableId = getReferenceId(lookupTable)
lookupTableInstance = serializeInstance(
mapper, lookupTable, lookupTableId, context, depth + 1
)
if lookupTableInstance:
dependencies.append(lookupTableInstance)
calls.append(
["setLookupTable", [wrapId(lookupTableId)]]
)
if dataObjectInstance:
colorArrayName = (
mapper.GetArrayName()
if mapper.GetArrayAccessMode() == 1
else mapper.GetArrayId()
)
return {
"parent": getReferenceId(parent),
"id": mapperId,
"type": class_name(mapper),
"properties": {
"resolveCoincidentTopology": mapper.GetResolveCoincidentTopology(),
"renderTime": mapper.GetRenderTime(),
"arrayAccessMode": mapper.GetArrayAccessMode(),
"scalarRange": mapper.GetScalarRange(),
"useLookupTableScalarRange": 1
if mapper.GetUseLookupTableScalarRange()
else 0,
"scalarVisibility": mapper.GetScalarVisibility(),
"colorByArrayName": colorArrayName,
"colorMode": mapper.GetColorMode(),
"scalarMode": mapper.GetScalarMode(),
"interpolateScalarsBeforeMapping": 1
if mapper.GetInterpolateScalarsBeforeMapping()
else 0,
},
"calls": calls,
"dependencies": dependencies,
}
return None
# -----------------------------------------------------------------------------
def genericVolumeMapperSerializer(parent, mapper, mapperId, context, depth):
# This kind of mapper requires us to get 2 items: input data and lookup
# table
dataObject = None
dataObjectInstance = None
lookupTableInstance = None
calls = []
dependencies = []
if hasattr(mapper, "GetInputDataObject"):
mapper.GetInputAlgorithm().Update()
dataObject = mapper.GetInputDataObject(0, 0)
else:
logger.debug("This mapper does not have GetInputDataObject method")
if dataObject:
dataObjectId = "%s-dataset" % mapperId
dataObjectInstance = serializeInstance(
mapper, dataObject, dataObjectId, context, depth + 1
)
if dataObjectInstance:
dependencies.append(dataObjectInstance)
calls.append(["setInputData", [wrapId(dataObjectId)]])
if dataObjectInstance:
return {
"parent": getReferenceId(parent),
"id": mapperId,
"type": class_name(mapper),
"properties": {
# VolumeMapper
"sampleDistance": mapper.GetSampleDistance(),
"imageSampleDistance": mapper.GetImageSampleDistance(),
# "maximumSamplesPerRay": mapper.GetMaximumSamplesPerRay(),
"autoAdjustSampleDistances": mapper.GetAutoAdjustSampleDistances(),
"blendMode": mapper.GetBlendMode(),
# "ipScalarRange": mapper.GetIpScalarRange(),
# "filterMode": mapper.GetFilterMode(),
# "preferSizeOverAccuracy": mapper.Get(),
},
"calls": calls,
"dependencies": dependencies,
}
return None
# -----------------------------------------------------------------------------
def lookupTableSerializer(parent, lookupTable, lookupTableId, context, depth):
# No children in this case, so no additions to bindings and return empty list
# But we do need to add instance
lookupTableRange = lookupTable.GetRange()
lookupTableHueRange = [0.5, 0]
if hasattr(lookupTable, "GetHueRange"):
try:
lookupTable.GetHueRange(lookupTableHueRange)
except Exception as inst:
pass
lutSatRange = lookupTable.GetSaturationRange()
lutAlphaRange = lookupTable.GetAlphaRange()
return {
"parent": getReferenceId(parent),
"id": lookupTableId,
"type": class_name(lookupTable),
"properties": {
"numberOfColors": lookupTable.GetNumberOfColors(),
"valueRange": lookupTableRange,
"hueRange": lookupTableHueRange,
# 'alphaRange': lutAlphaRange, # Causes weird rendering artifacts on client
"saturationRange": lutSatRange,
"nanColor": lookupTable.GetNanColor(),
"belowRangeColor": lookupTable.GetBelowRangeColor(),
"aboveRangeColor": lookupTable.GetAboveRangeColor(),
"useAboveRangeColor": True
if lookupTable.GetUseAboveRangeColor()
else False,
"useBelowRangeColor": True
if lookupTable.GetUseBelowRangeColor()
else False,
"alpha": lookupTable.GetAlpha(),
"vectorSize": lookupTable.GetVectorSize(),
"vectorComponent": lookupTable.GetVectorComponent(),
"vectorMode": lookupTable.GetVectorMode(),
"indexedLookup": lookupTable.GetIndexedLookup(),
},
}
# -----------------------------------------------------------------------------
def lookupTableToColorTransferFunction(lookupTable):
dataTable = lookupTable.GetTable()
table = dataTableToList(dataTable)
if table:
ctf = vtkColorTransferFunction()
tableRange = lookupTable.GetTableRange()
points = linspace(*tableRange, num=len(table))
for x, rgba in zip(points, table):
ctf.AddRGBPoint(x, *[x / 255 for x in rgba[:3]])
return ctf
return None
def lookupTableSerializer2(parent, lookupTable, lookupTableId, context, depth):
ctf = lookupTableToColorTransferFunction(lookupTable)
if ctf:
return colorTransferFunctionSerializer(
parent, ctf, lookupTableId, context, depth
)
return None
# -----------------------------------------------------------------------------
def propertySerializer(parent, propObj, propObjId, context, depth):
representation = (
propObj.GetRepresentation() if hasattr(propObj, "GetRepresentation") else 2
)
colorToUse = (
propObj.GetDiffuseColor() if hasattr(propObj, "GetDiffuseColor") else [1, 1, 1]
)
if representation == 1 and hasattr(propObj, "GetColor"):
colorToUse = propObj.GetColor()
return {
"parent": getReferenceId(parent),
"id": propObjId,
"type": class_name(propObj),
"properties": {
"representation": representation,
"diffuseColor": colorToUse,
"color": propObj.GetColor(),
"ambientColor": propObj.GetAmbientColor(),
"specularColor": propObj.GetSpecularColor(),
"edgeColor": propObj.GetEdgeColor(),
"ambient": propObj.GetAmbient(),
"diffuse": propObj.GetDiffuse(),
"specular": propObj.GetSpecular(),
"specularPower": propObj.GetSpecularPower(),
"opacity": propObj.GetOpacity(),
"interpolation": propObj.GetInterpolation(),
"edgeVisibility": 1 if propObj.GetEdgeVisibility() else 0,
"backfaceCulling": 1 if propObj.GetBackfaceCulling() else 0,
"frontfaceCulling": 1 if propObj.GetFrontfaceCulling() else 0,
"pointSize": propObj.GetPointSize(),
"lineWidth": propObj.GetLineWidth(),
"lighting": 1 if propObj.GetLighting() else 0,
},
}
def volumePropertySerializer(parent, propObj, propObjId, context, depth):
calls = []
dependencies = []
# Color handling
lut = propObj.GetRGBTransferFunction()
if lut:
lookupTableId = getReferenceId(lut)
lookupTableInstance = serializeInstance(
propObj, lut, lookupTableId, context, depth + 1
)
if lookupTableInstance:
dependencies.append(lookupTableInstance)
calls.append(["setRGBTransferFunction", [0, wrapId(lookupTableId)]])
# Piecewise handling
pwf = propObj.GetScalarOpacity()
if pwf:
pwfId = getReferenceId(pwf)
pwfInstance = serializeInstance(
propObj, pwf, pwfId, context, depth + 1
)
if pwfInstance:
dependencies.append(pwfInstance)
calls.append(["setScalarOpacity", [0, wrapId(pwfId)]])
return {
"parent": getReferenceId(parent),
"id": propObjId,
"type": class_name(propObj),
"properties": {
"independentComponents": propObj.GetIndependentComponents(),
"interpolationType": propObj.GetInterpolationType(),
"shade": propObj.GetShade(),
"ambient": propObj.GetAmbient(),
"diffuse": propObj.GetDiffuse(),
"specular": propObj.GetSpecular(),
"specularPower": propObj.GetSpecularPower(),
# "useLabelOutline": propObj.GetUseLabelOutline(),
# "labelOutlineThickness": propObj.GetLabelOutlineThickness(),
},
"calls": calls,
"dependencies": dependencies,
}
# -----------------------------------------------------------------------------
def imagedataSerializer(parent, dataset, datasetId, context, depth, requested_fields = ["Normals", "TCoords"]):
if hasattr(dataset, "GetDirectionMatrix"):
direction = [dataset.GetDirectionMatrix().GetElement(0, i) for i in range(9)]
else:
direction = [1, 0, 0, 0, 1, 0, 0, 0, 1]
# Extract dataset fields
fields = []
extractRequiredFields(fields, parent, dataset, context, "*")
return {
"parent": getReferenceId(parent),
"id": datasetId,
"type": class_name(dataset),
"properties": {
"spacing": dataset.GetSpacing(),
"origin": dataset.GetOrigin(),
"dimensions": dataset.GetDimensions(),
"direction": direction,
"fields": fields,
},
}
# -----------------------------------------------------------------------------
def polydataSerializer(parent, dataset, datasetId, context, depth, requested_fields = ["Normals", "TCoords"]):
if dataset and dataset.GetPoints():
properties = {}
# Points
points = getArrayDescription(dataset.GetPoints().GetData(), context)
points["vtkClass"] = "vtkPoints"
properties["points"] = points
# Verts
if dataset.GetVerts() and dataset.GetVerts().GetData().GetNumberOfTuples() > 0:
_verts = getArrayDescription(dataset.GetVerts().GetData(), context)
properties["verts"] = _verts
properties["verts"]["vtkClass"] = "vtkCellArray"
# Lines
if dataset.GetLines() and dataset.GetLines().GetData().GetNumberOfTuples() > 0:
_lines = getArrayDescription(dataset.GetLines().GetData(), context)
properties["lines"] = _lines
properties["lines"]["vtkClass"] = "vtkCellArray"
# Polys
if dataset.GetPolys() and dataset.GetPolys().GetData().GetNumberOfTuples() > 0:
_polys = getArrayDescription(dataset.GetPolys().GetData(), context)
properties["polys"] = _polys
properties["polys"]["vtkClass"] = "vtkCellArray"
# Strips
if (
dataset.GetStrips()
and dataset.GetStrips().GetData().GetNumberOfTuples() > 0
):
_strips = getArrayDescription(dataset.GetStrips().GetData(), context)
properties["strips"] = _strips
properties["strips"]["vtkClass"] = "vtkCellArray"
# Fields
properties["fields"] = []
extractRequiredFields(properties["fields"], parent, dataset, context, requested_fields)
return {
"parent": getReferenceId(parent),
"id": datasetId,
"type": class_name(dataset),
"properties": properties,
}
logger.debug("This dataset has no points!")
return None
# -----------------------------------------------------------------------------
def mergeToPolydataSerializer(parent, dataObject, dataObjectId, context, depth, requested_fields=["Normals", "TCoords"]):
dataset = None
if dataObject.IsA("vtkCompositeDataSet"):
gf = vtkCompositeDataGeometryFilter()
gf.SetInputData(dataObject)
gf.Update()
dataset = gf.GetOutput()
elif dataObject.IsA("vtkUnstructuredGrid"):
gf = vtkDataSetSurfaceFilter()
gf.SetInputData(dataObject)
gf.Update()
dataset = gf.GetOutput()
else:
dataset = mapper.GetInput()
return polydataSerializer(parent, dataset, dataObjectId, context, depth, requested_fields)
# -----------------------------------------------------------------------------
def colorTransferFunctionSerializer(parent, instance, objId, context, depth):
nodes = []
for i in range(instance.GetSize()):
# x, r, g, b, midpoint, sharpness
node = [0, 0, 0, 0, 0, 0]
instance.GetNodeValue(i, node)
nodes.append(node)
return {
"parent": getReferenceId(parent),
"id": objId,
"type": class_name(instance),
"properties": {
"clamping": 1 if instance.GetClamping() else 0,
"colorSpace": instance.GetColorSpace(),
"hSVWrap": 1 if instance.GetHSVWrap() else 0,
# 'nanColor': instance.GetNanColor(), # Breaks client
# 'belowRangeColor': instance.GetBelowRangeColor(), # Breaks client
# 'aboveRangeColor': instance.GetAboveRangeColor(), # Breaks client
# 'useAboveRangeColor': 1 if instance.GetUseAboveRangeColor() else 0,
# 'useBelowRangeColor': 1 if instance.GetUseBelowRangeColor() else 0,
"allowDuplicateScalars": 1 if instance.GetAllowDuplicateScalars() else 0,
"alpha": instance.GetAlpha(),
"vectorComponent": instance.GetVectorComponent(),
"vectorSize": instance.GetVectorSize(),
"vectorMode": instance.GetVectorMode(),
"indexedLookup": instance.GetIndexedLookup(),
"nodes": nodes,
},
}
def discretizableColorTransferFunctionSerializer(parent, instance, objId, context, depth):
ctf = colorTransferFunctionSerializer(parent, instance, objId, context, depth)
ctf["properties"]["discretize"] = instance.GetDiscretize()
ctf["properties"]["numberOfValues"] = instance.GetNumberOfValues()
return ctf
# -----------------------------------------------------------------------------
def pwfSerializer(parent, instance, objId, context, depth):
nodes = []
for i in range(instance.GetSize()):
# x, y, midpoint, sharpness
node = [0, 0, 0, 0]
instance.GetNodeValue(i, node)
nodes.append(node)
return {
"parent": getReferenceId(parent),
"id": objId,
"type": class_name(instance),
"properties": {
"range": list(instance.GetRange()),
"clamping": instance.GetClamping(),
"allowDuplicateScalars": instance.GetAllowDuplicateScalars(),
"nodes": nodes,
},
}
# -----------------------------------------------------------------------------
def cubeAxesSerializer(parent, actor, actorId, context, depth):
"""
Possible add-on properties for vtk.js:
gridLines: True,
axisLabels: None,
axisTitlePixelOffset: 35.0,
axisTextStyle: {
fontColor: 'white',
fontStyle: 'normal',
fontSize: 18,
fontFamily: 'serif',
},
tickLabelPixelOffset: 12.0,
tickTextStyle: {
fontColor: 'white',
fontStyle: 'normal',
fontSize: 14,
fontFamily: 'serif',
},
"""
axisLabels = ["", "", ""]
if actor.GetXAxisLabelVisibility():
axisLabels[0] = actor.GetXTitle()
if actor.GetYAxisLabelVisibility():
axisLabels[1] = actor.GetYTitle()
if actor.GetZAxisLabelVisibility():
axisLabels[2] = actor.GetZTitle()
return {
"parent": getReferenceId(parent),
"id": actorId,
"type": "vtkCubeAxesActor",
"properties": {
# vtkProp
"visibility": actor.GetVisibility(),
"pickable": actor.GetPickable(),
"dragable": actor.GetDragable(),
"useBounds": actor.GetUseBounds(),
# vtkProp3D
"origin": actor.GetOrigin(),
"position": actor.GetPosition(),
"scale": actor.GetScale(),
# vtkActor
"forceOpaque": actor.GetForceOpaque(),
"forceTranslucent": actor.GetForceTranslucent(),
# vtkCubeAxesActor
"dataBounds": actor.GetBounds(),
"faceVisibilityAngle": 8,
"gridLines": True,
"axisLabels": axisLabels,
"axisTitlePixelOffset": 35.0,
"axisTextStyle": {
"fontColor": "white",
"fontStyle": "normal",
"fontSize": 18,
"fontFamily": "serif",
},
"tickLabelPixelOffset": 12.0,
"tickTextStyle": {
"fontColor": "white",
"fontStyle": "normal",
"fontSize": 14,
"fontFamily": "serif",
},
},
"calls": [["setCamera", [wrapId(getReferenceId(actor.GetCamera()))]]],
"dependencies": [],
}
# -----------------------------------------------------------------------------
def scalarBarActorSerializer(parent, actor, actorId, context, depth):
dependencies = []
calls = []
lut = actor.GetLookupTable()
if not lut:
return None
lutId = getReferenceId(lut)
lutInstance = serializeInstance(actor, lut, lutId, context, depth + 1)
if not lutInstance:
return None
dependencies.append(lutInstance)
calls.append(["setScalarsToColors", [wrapId(lutId)]])
prop = None
if hasattr(actor, "GetProperty"):
prop = actor.GetProperty()
else:
logger.debug("This scalarBarActor does not have a GetProperty method")
if prop:
propId = getReferenceId(prop)
propertyInstance = serializeInstance(
actor, prop, propId, context, depth + 1
)
if propertyInstance:
dependencies.append(propertyInstance)
calls.append(["setProperty", [wrapId(propId)]])
axisLabel = actor.GetTitle()
width = actor.GetWidth()
height = actor.GetHeight()
return {
"parent": getReferenceId(parent),
"id": actorId,
"type": "vtkScalarBarActor",
"properties": {
# vtkProp
"visibility": actor.GetVisibility(),
"pickable": actor.GetPickable(),
"dragable": actor.GetDragable(),
"useBounds": actor.GetUseBounds(),
# vtkActor2D
# "position": actor.GetPosition(),
# "position2": actor.GetPosition2(),
# "width": actor.GetWidth(),
# "height": actor.GetHeight(),
# vtkScalarBarActor
"automated": True,
"axisLabel": axisLabel,
# 'barPosition': [0, 0],
# 'barSize': [0, 0],
"boxPosition": [0.88, -0.92],
"boxSize": [width, height],
"axisTitlePixelOffset": 36.0,
"axisTextStyle": {
"fontColor": actor.GetTitleTextProperty().GetColor(),
"fontStyle": "normal",
"fontSize": 18,
"fontFamily": "serif",
},
"tickLabelPixelOffset": 14.0,
"tickTextStyle": {
"fontColor": actor.GetTitleTextProperty().GetColor(),
"fontStyle": "normal",
"fontSize": 14,
"fontFamily": "serif",
},
"drawNanAnnotation": actor.GetDrawNanAnnotation(),
"drawBelowRangeSwatch": actor.GetDrawBelowRangeSwatch(),
"drawAboveRangeSwatch": actor.GetDrawAboveRangeSwatch(),
},
"calls": calls,
"dependencies": dependencies,
}
# -----------------------------------------------------------------------------
def rendererSerializer(parent, instance, objId, context, depth):
dependencies = []
viewPropIds = []
lightsIds = []
calls = []
# Camera
camera = instance.GetActiveCamera()
cameraId = getReferenceId(camera)
cameraInstance = serializeInstance(instance, camera, cameraId, context, depth + 1)
if cameraInstance:
dependencies.append(cameraInstance)
calls.append(["setActiveCamera", [wrapId(cameraId)]])
# View prop as representation containers
viewPropCollection = instance.GetViewProps()
for rpIdx in range(viewPropCollection.GetNumberOfItems()):
viewProp = viewPropCollection.GetItemAsObject(rpIdx)
viewPropId = getReferenceId(viewProp)
viewPropInstance = serializeInstance(
instance, viewProp, viewPropId, context, depth + 1
)
if viewPropInstance:
dependencies.append(viewPropInstance)
viewPropIds.append(viewPropId)
calls += context.buildDependencyCallList(
"%s-props" % objId, viewPropIds, "addViewProp", "removeViewProp"
)
# Lights
lightCollection = instance.GetLights()
for lightIdx in range(lightCollection.GetNumberOfItems()):
light = lightCollection.GetItemAsObject(lightIdx)
lightId = getReferenceId(light)
lightInstance = serializeInstance(instance, light, lightId, context, depth + 1)
if lightInstance:
dependencies.append(lightInstance)
lightsIds.append(lightId)
calls += context.buildDependencyCallList(
"%s-lights" % objId, lightsIds, "addLight", "removeLight"
)
if len(dependencies) > 1:
return {
"parent": getReferenceId(parent),
"id": objId,
"type": class_name(instance),
"properties": {
"background": instance.GetBackground(),
"background2": instance.GetBackground2(),
"viewport": instance.GetViewport(),
# These commented properties do not yet have real setters in vtk.js
# 'gradientBackground': instance.GetGradientBackground(),
# 'aspect': instance.GetAspect(),
# 'pixelAspect': instance.GetPixelAspect(),
# 'ambient': instance.GetAmbient(),
"twoSidedLighting": instance.GetTwoSidedLighting(),
"lightFollowCamera": instance.GetLightFollowCamera(),
"layer": instance.GetLayer(),
"preserveColorBuffer": instance.GetPreserveColorBuffer(),
"preserveDepthBuffer": instance.GetPreserveDepthBuffer(),
"nearClippingPlaneTolerance": instance.GetNearClippingPlaneTolerance(),
"clippingRangeExpansion": instance.GetClippingRangeExpansion(),
"useShadows": instance.GetUseShadows(),
"useDepthPeeling": instance.GetUseDepthPeeling(),
"occlusionRatio": instance.GetOcclusionRatio(),
"maximumNumberOfPeels": instance.GetMaximumNumberOfPeels(),
"interactive": instance.GetInteractive(),
},
"dependencies": dependencies,
"calls": calls,
}
return None
# -----------------------------------------------------------------------------
def cameraSerializer(parent, instance, objId, context, depth):
return {
"parent": getReferenceId(parent),
"id": objId,
"type": class_name(instance),
"properties": {
"focalPoint": instance.GetFocalPoint(),
"position": instance.GetPosition(),
"viewUp": instance.GetViewUp(),
"clippingRange": instance.GetClippingRange(),
},
}
# -----------------------------------------------------------------------------
def lightTypeToString(value):
"""
#define VTK_LIGHT_TYPE_HEADLIGHT 1
#define VTK_LIGHT_TYPE_CAMERA_LIGHT 2
#define VTK_LIGHT_TYPE_SCENE_LIGHT 3
'HeadLight';
'SceneLight';
'CameraLight'
"""
if value == 1:
return "HeadLight"
elif value == 2:
return "CameraLight"
return "SceneLight"
def lightSerializer(parent, instance, objId, context, depth):
return {
"parent": getReferenceId(parent),
"id": objId,
"type": class_name(instance),
"properties": {
# 'specularColor': instance.GetSpecularColor(),
# 'ambientColor': instance.GetAmbientColor(),
"switch": instance.GetSwitch(),
"intensity": instance.GetIntensity(),
"color": instance.GetDiffuseColor(),
"position": instance.GetPosition(),
"focalPoint": instance.GetFocalPoint(),
"positional": instance.GetPositional(),
"exponent": instance.GetExponent(),
"coneAngle": instance.GetConeAngle(),
"attenuationValues": instance.GetAttenuationValues(),
"lightType": lightTypeToString(instance.GetLightType()),
"shadowAttenuation": instance.GetShadowAttenuation(),
},
}
# -----------------------------------------------------------------------------
def renderWindowSerializer(parent, instance, objId, context, depth):
dependencies = []
rendererIds = []
rendererCollection = instance.GetRenderers()
for rIdx in range(rendererCollection.GetNumberOfItems()):
# Grab the next vtkRenderer
renderer = rendererCollection.GetItemAsObject(rIdx)
rendererId = getReferenceId(renderer)
rendererInstance = serializeInstance(
instance, renderer, rendererId, context, depth + 1
)
if rendererInstance:
dependencies.append(rendererInstance)
rendererIds.append(rendererId)
calls = context.buildDependencyCallList(
objId, rendererIds, "addRenderer", "removeRenderer"
)
return {
"parent": getReferenceId(parent),
"id": objId,
"type": class_name(instance),
"properties": {"numberOfLayers": instance.GetNumberOfLayers()},
"dependencies": dependencies,
"calls": calls,
"mtime": instance.GetMTime(),
}