From 1ca83faddc5f2fef6518817ea2c072da3a676c72 Mon Sep 17 00:00:00 2001 From: Claire Kuang Date: Fri, 29 Oct 2021 15:25:19 +0100 Subject: [PATCH] finalized diff logic --- server/__pycache__/mesh_diff.cpython-39.pyc | Bin 4892 -> 6013 bytes server/mesh_diff.py | 149 +++++++++++++++----- 2 files changed, 111 insertions(+), 38 deletions(-) diff --git a/server/__pycache__/mesh_diff.cpython-39.pyc b/server/__pycache__/mesh_diff.cpython-39.pyc index c4102223535e83f98fadeacfc631f6ffbbf82d37..8654edc936a06a875355858eaa618a2c1a1aeb75 100644 GIT binary patch literal 6013 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 delta 2753 zcmZuz-ESMm5#PNp9=|_DiTbc)*^y1FR+Khq99QvIWF-x3Cw2UiD*~40o+!!`N$s5j z*;s-In7+k8ntMqLJt&2nx4twVKmxRXK!Ls&hdvZ2P@q7Y0tNckqW#U0R@kJs*twbC z?#%4&&CcAv*A7=!>*;iY;koyRhxLya82b|mlTQr76?9MiWc3#V&IA+b_Wa(0vk>u_ z_KCekXEEZl?ImYP_hr4~^}5oRCrQ?& z>&ZD9j>vrQrEzMo0=ddVv$4}^iw1}EyX;6u%O4bo>cKEQBnVoEMjx1pX> z23%a}n?OvT489*3G?jU5P{N=Y89ai)+-My3CT0+dbJcEzNzw9p?fQe8uD4T-J2Oq! zuZiF}Yw3Iuo0EBv(5m-(ZbyV#ON6nVx>xsoDbLb~(QbKu)skm|53J=o6Q@Xn8M0Mx zw`+T_?Rx9Lywf0z8=B3jCD005f*ZU9j^vS4=JGq3SLJdoD8znM&Or$ILC7D3{GmKY zLnem8*nD!rXTa^J^H*t53uBG@Qo0>qz7Sls>#G-m%I^``pmAMZBmM;F1H&oQI}JB77IN`F_CAe2V{ehJE>8qs z#-ACGGrGG`2%Xj#^VyK~b$n3@9Brs0a5QBMdGwVD>y5PQ{VnZ&V zgQ$3=pBQ82G4^afsbVUDE2~wQGF7s|2E^Xeq@(O3Qza+{RUBNBI0VerzO%mt2dQru zTkaGYoTI##tinYATV)?~r`2$+NiS?c6 z&#GBEI_!6}$Na<||KS(t%PUP~RZiuH7VfKYOHwY9-vw#@?5Fg6ez{tdl=UR#tmxXv z=ZL#Ngsw`Ms?{3Zj$dzeyjo3OHEEy}3^M2WR`AQrT48RK9bY2+Wg@e|-!caSd6|^? zu2bwu_pMgh zA5e0zl(ox`G=*E%k5Rk|?H#6Kn}|b1qOEN2BF)$}6!T4?3*(W_nQuWej-(P7<_1H} zi3#@g~`qDZ*A4C-M)J3#g~zcwYBw--~IYWif=UCj(gCP zm%a#E#T#oQP2qYC+3NYNZU;$xbWV=p)%RSg)Z6eds^`I9imwimQRIi#U6l03jyzAk z^?K)lV>P5(_g(oq5E&IRI#?N}35JJS7S;Z0`u}|(`2(7In+Ul=>_ygH&&i76s3`?iA6`Ha2^Apy|LF7_uK9z$~hk8lN4?awH!}rc<0CBJiRqs OAtR_wPwC0R?f(Mnmv2e{ diff --git a/server/mesh_diff.py b/server/mesh_diff.py index 32f742b..f96c7e6 100644 --- a/server/mesh_diff.py +++ b/server/mesh_diff.py @@ -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)