This commit is contained in:
Claire Kuang 2021-10-29 15:25:19 +01:00
Родитель ce3cf66db7
Коммит 1ca83faddc
2 изменённых файлов: 111 добавлений и 38 удалений

Двоичные данные
server/__pycache__/mesh_diff.cpython-39.pyc

Двоичный файл не отображается.

Просмотреть файл

@ -8,19 +8,20 @@ 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.models import Branch
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.geometry import GEOMETRY, Box, Brep, Point, Mesh, Line
from specklepy.objects import Base
from specklepy.objects.other import RenderMaterial
import os
URL = 'https://speckle.xyz/streams/'
HOST = "speckle.xyz"
#STREAM_ID = "8325294b8f"
#COMMIT_ID = "d930269725" # current commit
#PREV_COMMIT_ID = "e9d8f67969" # previous commit
STREAM_ID = "8325294b8f"
COMMIT_ID = "c207299871" # current commit
PREV_COMMIT_ID = "b9f376d75d" # previous commit
DIFF_BRANCH = "diff"
COLORS = [-6426, -13108, -19790, -26215, -32640, -39322, -45747, -52429, -59111, -65536]
WHITE = -1
@ -45,27 +46,27 @@ def receive_data(
return res
def get_all_meshes(child:Base):
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 isinstance(prop, Brep):
if not hasattr(prop, "displayMesh"):
break
meshes.append((prop.displayMesh, prop.id))
meshes.append((prop.displayMesh, prop.id, prop.applicationId, prop))
elif isinstance(prop, Mesh):
meshes.append((prop, prop.id, prop.applicationId))
elif isinstance(prop, list):
for p in prop:
if isinstance(p, Mesh):
meshes.append((p, p.id))
else:
if isinstance(p, Brep):
if not hasattr(p, "displayMesh"):
break
meshes.append((p.displayMesh, p.id))
meshes.append((p.displayMesh, p.id, p.applicationId, p))
elif isinstance(p, Mesh):
meshes.append((p, p.id, p.applicationId))
return meshes
def get_all_points(meshes: List[Mesh]):
@ -95,6 +96,19 @@ def find_closest_point(current: Point, points: List[Point]):
smallest_distance = d
return smallest_distance
def check_existing_commits(
client: SpeckleClient, stream_id: str, commit_current: str, commit_previous: str
) -> Any:
transport = ServerTransport(client, stream_id)
branch_commits: Branch = client.branch.get(stream_id, DIFF_BRANCH, 50)
for commit in branch_commits.commits.items:
if commit.message == f"{commit_current}-{commit_previous}":
return commit.id
return None
def compare_meshes(stream_id: str, commit_current: str, commit_previous: str):
client = get_authenticated_client()
@ -103,6 +117,10 @@ def compare_meshes(stream_id: str, commit_current: str, commit_previous: str):
# query for latest x commits in diff branch
# read commit message & parse
# return url if found
existing_commit = check_existing_commits(client, stream_id, commit_current, commit_previous)
if existing_commit is not None:
url = URL + stream_id + '/commits/' + existing_commit
return url
# get meshes from commits
previous_commit = receive_data(client, stream_id, commit_previous)
@ -111,49 +129,73 @@ def compare_meshes(stream_id: str, commit_current: str, commit_previous: str):
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
# if object id has changed, check for application id - if these are the same, compare these objects directly
matched_current_indices = []
matched_previous_indices = []
paired_current_indices = []
paired_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
elif current_meshes[i][2] == previous_meshes[j][2]:
paired_current_indices.append(i)
paired_previous_indices.append(j)
break
# remove matched previous meshes and get list of all mesh points from processed list
previous_meshes_ref = []
# remove matched previous meshes and matched pairs and get list of all mesh points from processed list
# this will be used as reference for all meshes that have changed and don't have a specific match to compare to
previous_meshes_ref_pool = []
for i in range(0, len(previous_meshes), 1):
if matched_previous_indices.__contains__(i):
if matched_previous_indices.__contains__(i) or paired_previous_indices.__contains__(i):
continue
previous_meshes_ref.append(previous_meshes[i][0])
previous_points = get_all_points(previous_meshes_ref)
previous_meshes_ref_pool.append(previous_meshes[i][0])
ref_pool = get_all_points(previous_meshes_ref_pool)
# 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
# for each mesh in the current commit, compare mesh vertices with ref pool or matched pair to determine scale of change
diff_meshes = []
match_meshes = []
prev_meshes = []
same_meshes = []
ref_meshes = []
diff_mesh_pairs = []
diff_mesh_ref_indices = [] # the corresponding ref pair mesh to diff mesh pairs
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)
same_meshes.append(mesh)
continue
diff_mesh = mesh
vertices = get_all_points([mesh])
diff_mesh_colors = [WHITE] * (len(vertices))
diff_values = []
# check for pairing
paired_mesh_points = []
paired_ref_mesh_index = None
is_paired = False
if paired_current_indices.__contains__(i):
paired_ref_mesh_index = paired_previous_indices[paired_current_indices.index(i)]
paired_mesh_points = get_all_points([previous_meshes[paired_ref_mesh_index][0]])
is_paired = True
for vertex in vertices:
diff_values.append(find_closest_point(vertex, previous_points))
if is_paired:
diff_values.append(find_closest_point(vertex, paired_mesh_points))
else:
diff_values.append(find_closest_point(vertex, ref_pool))
# determine color value for vertex by remapping domain
changed = False
bin_size = max(diff_values) / len(COLORS)
for i in range(0, len(vertices), 1):
if diff_values[i] == 0:
@ -163,22 +205,46 @@ def compare_meshes(stream_id: str, commit_current: str, commit_previous: str):
if index == len(COLORS):
index -= 1
diff_mesh_colors[i] = COLORS[index]
changed = True
if not changed: # if hasn't changed, append to same list
mesh.renderMaterial = ghosted
if is_paired:
matched_previous_indices.append(paired_ref_mesh_index)
same_meshes.append(mesh)
# set colors and add mesh to list
diff_mesh.colors = diff_mesh_colors
diff_meshes.append(diff_mesh)
else: # set colors and add mesh to diff list or paired diff list
diff_mesh.colors = diff_mesh_colors
if is_paired:
diff_mesh_pairs.append(diff_mesh)
diff_mesh_ref_indices.append(paired_ref_mesh_index)
else:
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]
# process reference meshes
diff_mesh_refs = []
for j in range(0, len(previous_meshes)):
if matched_previous_indices.__contains__(j) or diff_mesh_ref_indices.__contains__(j): # skip matched reference meshes and paired refs
continue
mesh = previous_meshes[j][0]
mesh.renderMaterial = ghosted
prev_meshes.append(mesh)
ref_meshes.append(mesh)
for diff_mesh_ref_index in diff_mesh_ref_indices:
mesh = previous_meshes[diff_mesh_ref_index]
if len(mesh) > 3:
diff_mesh_refs.append(mesh[3])
else:
diff_mesh_refs.append(mesh[0])
# get units from first mesh in current commit
units = current_meshes[0][0].units
# create a new commit with the diff meshes and changed edges
return send_diff_data(stream_id, commit_current, commit_previous, diff_meshes, match_meshes, prev_meshes)
# create a new commit with the diff meshes
return send_diff_data(stream_id, commit_current, commit_previous, units, diff_meshes, diff_mesh_pairs, diff_mesh_refs, same_meshes, ref_meshes)
def send_diff_data(stream_id: str, commit_current: str, commit_previous: str, meshes: List[Mesh], unchanged: List[Mesh], prev: List[Mesh]):
def send_diff_data(stream_id: str, commit_current: str, commit_previous: str, units: str, changed: List[Mesh], changed_pairs: List[Mesh], ref_pairs: List[Base], unchanged: List[Mesh], ref: List[Mesh]):
client = get_authenticated_client()
# create a branch if necessary
@ -191,9 +257,15 @@ def send_diff_data(stream_id: str, commit_current: str, commit_previous: str, me
# create a commit with message "current_commit_id - previous_commit_id"
base = Base()
base["changed"] = meshes
base.units = units
base["changed"] = changed
for i in range(0, len(changed_pairs), 1):
layer = f"changed::{i}"
base[f"{layer}::changed"] = [changed_pairs[i]]
base[f"{layer}::ref"] = [ref_pairs[i]]
base["same"] = unchanged
base["ref"] = prev
base["ref"] = ref
transport = ServerTransport(client=client, stream_id=stream_id)
@ -206,7 +278,8 @@ def send_diff_data(stream_id: str, commit_current: str, commit_previous: str, me
message= commit_current + "-" + commit_previous
)
return 'https://speckle.xyz/streams/' + stream_id + '/commits/' + commit_id
return URL + stream_id + '/commits/' + commit_id
# uncomment for debug
# compare_meshes(STREAM_ID, COMMIT_ID, PREV_COMMIT_ID)