Create mesh_diff.py
This commit is contained in:
Родитель
904219cdaf
Коммит
3aa6b100f3
|
@ -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()
|
||||
|
||||
|
Загрузка…
Ссылка в новой задаче