This commit is contained in:
Claire Kuang 2021-10-28 17:37:32 +01:00
Родитель 904219cdaf
Коммит 3aa6b100f3
1 изменённых файлов: 208 добавлений и 0 удалений

208
mesh_diff.py Normal file
Просмотреть файл

@ -0,0 +1,208 @@
from array import array
from hashlib import new
from logging import NullHandler
from types import AsyncGeneratorType
from typing import Any, List
from math import floor
from specklepy.api import operations
from specklepy.api.client import SpeckleClient
from specklepy.api.credentials import get_default_account
from specklepy.api.resources import stream
from specklepy.transports.server import ServerTransport
from specklepy.objects.geometry import GEOMETRY, Box, Point, Mesh, Line
from specklepy.objects import Base
from specklepy.objects.other import RenderMaterial
import os
HOST = "speckle.xyz"
STREAM_ID = "8325294b8f"
COMMIT_ID = "d930269725" # current commit
PREV_COMMIT_ID = "e9d8f67969" # previous commit
DIFF_BRANCH = "diff"
COLORS = [-6426, -13108, -19790, -26215, -32640, -39322, -45747, -52429, -59111, -65536]
WHITE = -1
def get_authenticated_client() -> SpeckleClient:
client = SpeckleClient(host=HOST)
account = get_default_account()
client.authenticate(token=account.token)
return client
def receive_data(
client: SpeckleClient, stream_id: str = STREAM_ID, commit_id: str = COMMIT_ID
) -> Any:
transport = ServerTransport(client, stream_id)
commit = client.commit.get(stream_id, commit_id)
res = operations.receive(commit.referencedObject, transport)
# if grasshopper, will be nested under data: res["data"]
# if rhino/autocad/revit, will be sent with layers or categories
return res
def get_all_meshes(child:Base):
meshes = []
names = child.get_dynamic_member_names()
for name in names:
prop = child[name]
if isinstance(prop, Base):
if isinstance(prop, Mesh):
meshes.append((prop, prop.id))
else:
if not hasattr(prop, "displayMesh"):
break
meshes.append((prop.displayMesh, prop.id))
elif isinstance(prop, list):
for p in prop:
if isinstance(p, Mesh):
meshes.append((p, p.id))
else:
if not hasattr(p, "displayMesh"):
break
meshes.append((p.displayMesh, p.id))
return meshes
def get_all_points(meshes: List[Mesh]):
points = []
for mesh in meshes:
for i in range(2, len(mesh.vertices), 3):
point = Point()
point.x = mesh.vertices[i-2]
point.y = mesh.vertices[i-1]
point.z = mesh.vertices[i]
points.append(point)
return points
def find_point(current: Point, points: List[Point]):
for point in points:
if (point.x == current.x and point.y == current.y and point.z == current.z):
return True
return False
def find_closest_point(current: Point, points: List[Point]):
smallest_distance = None
for point in points:
d = ((current.x - point.x)**2 + (current.y - point.y)**2 + (current.z - point.z)**2)**0.5
if smallest_distance is not None:
if d > smallest_distance:
continue
smallest_distance = d
return smallest_distance
def compare_meshes():
client = get_authenticated_client()
# get meshes from commits
previous_commit = receive_data(client, STREAM_ID, PREV_COMMIT_ID)
previous_meshes = get_all_meshes(previous_commit)
current_commit = receive_data(client, STREAM_ID, COMMIT_ID)
current_meshes = get_all_meshes(current_commit)
# pre process meshes in the current commit to check for same object ID (this means obj hasn't changed) - skip these
matched_current_indices = []
matched_previous_indices = []
for i in range(0, len(current_meshes), 1):
for j in range(0, len(previous_meshes), 1):
if current_meshes[i][1] == previous_meshes[j][1]:
matched_current_indices.append(i)
matched_previous_indices.append(j)
break
# remove matched previous meshes and get list of all mesh points from processed list
previous_meshes_ref = []
for i in range(0, len(previous_meshes), 1):
if matched_previous_indices.__contains__(i):
continue
previous_meshes_ref.append(previous_meshes[i][0])
previous_points = get_all_points(previous_meshes_ref)
# create a ghosted render material
ghosted = RenderMaterial()
ghosted.diffuse = WHITE
ghosted.opacity = 0.1
# for each mesh in the current commit, compare face vertices with all previous points to determine edges that have changed
diff_meshes = []
match_meshes = []
prev_meshes = []
for i in range(0, len(current_meshes), 1):
mesh = current_meshes[i][0]
# send matched current meshes with rendermaterial semi-transparent (ghosted)
if matched_current_indices.__contains__(i):
mesh.renderMaterial = ghosted
match_meshes.append(mesh)
continue
diff_mesh = mesh
vertices = get_all_points([mesh])
diff_mesh_colors = [WHITE] * (len(vertices))
diff_values = []
for vertex in vertices:
diff_values.append(find_closest_point(vertex, previous_points))
# determine color value for vertex by remapping domain
bin_size = max(diff_values) / len(COLORS)
for i in range(0, len(vertices), 1):
if diff_values[i] == 0:
continue
else:
index = floor(diff_values[i] / bin_size)
if index == len(COLORS):
index -= 1
diff_mesh_colors[i] = COLORS[index]
# set colors and add mesh to list
diff_mesh.colors = diff_mesh_colors
diff_meshes.append(diff_mesh)
# send previous commit meshes as well, with rendermaterial semi-transparent (ghosted)
for previous_mesh in previous_meshes:
mesh = previous_mesh[0]
mesh.renderMaterial = ghosted
prev_meshes.append(mesh)
# create a new commit with the diff meshes and changed edges
send_diff_data(diff_meshes, match_meshes, prev_meshes)
def send_diff_data(meshes: List[Mesh], unchanged: List[Mesh], prev: List[Mesh]):
client = get_authenticated_client()
# create a branch if necessary
branches = client.branch.list(STREAM_ID)
has_res_branch = any(b.name == DIFF_BRANCH for b in branches)
if not has_res_branch:
client.branch.create(
STREAM_ID, name=DIFF_BRANCH, description="all your stream diff results"
)
# create a commit with message "current_commit_id - previous_commit_id"
base = Base()
base["changed"] = meshes
base["same"] = unchanged
base["ref"] = prev
transport = ServerTransport(client=client, stream_id=STREAM_ID)
hash = operations.send(base=base, transports=[transport])
commit_id = client.commit.create(
STREAM_ID,
hash, # object id
DIFF_BRANCH,
message= COMMIT_ID + "-" + PREV_COMMIT_ID
)
return 'https://speckle.xyz/streams/' + STREAM_ID + '/branches/' + DIFF_BRANCH
compare_meshes()