From 32eb5fb199fa65672806417d082e24c68f368cfc Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Fri, 29 Oct 2021 17:54:15 +0200 Subject: [PATCH 01/12] Isolated mesh_diff from test file plus simplified inputs in some functions --- server/main.py | 17 ++--- server/mesh_diff.py | 141 +++++++++++++++++++++------------------ server/test_mesh_diff.py | 11 +++ 3 files changed, 92 insertions(+), 77 deletions(-) create mode 100644 server/test_mesh_diff.py diff --git a/server/main.py b/server/main.py index e39e939..30b2e3c 100644 --- a/server/main.py +++ b/server/main.py @@ -1,7 +1,8 @@ -from typing import Optional +"""FastAPI Backend for the AEC Tech Masterclass""" + from fastapi import FastAPI, Request -from mesh_diff import compare_meshes from fastapi.middleware.cors import CORSMiddleware +import mesh_diff as md app = FastAPI() @@ -19,15 +20,9 @@ app.add_middleware( ) -@app.get("/") -def read_root(): - return {"Hello": "World"} - - @app.get("/diff/{stream_id}/{commit_current}/{commit_previous}") def get_diff(stream_id: str, commit_current: str, commit_previous: str, request: Request): - print(request) - print(request.headers.get("Authorisation")) + """Diffing endpoint""" token = request.headers.get("Authorisation").split(" ")[1] - print(token) - return compare_meshes(stream_id, commit_current, commit_previous, token) + md.authenticate(token) + return md.compare_meshes(stream_id, commit_current, commit_previous) diff --git a/server/mesh_diff.py b/server/mesh_diff.py index eb8a369..3729a6f 100644 --- a/server/mesh_diff.py +++ b/server/mesh_diff.py @@ -1,37 +1,29 @@ -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 - +import math 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, Brep, Point, Mesh, Line +from specklepy.objects.geometry import Brep, Point, Mesh from specklepy.objects import Base from specklepy.objects.other import RenderMaterial -import os - -URL = 'https://speckle.xyz/streams/' -HOST = "latest.speckle.dev" -STREAM_ID = "8325294b8f" -COMMIT_ID = "c207299871" # current commit -PREV_COMMIT_ID = "b9f376d75d" # previous commit +HOST = "https://speckle.xyz" DIFF_BRANCH = "diff" COLORS = [-6426, -13108, -19790, -26215, - 32640, -39322, -45747, -52429, -59111, -65536] WHITE = -1 +CLIENT: SpeckleClient = None + +_STREAM_ID: str = None +_COMMIT_CURRENT: str = None +_COMMIT_PREV: str = None -def get_authenticated_client(token: str) -> SpeckleClient: - client = SpeckleClient(host=HOST) - client.authenticate(token=token) - return client +def authenticate(token: str) -> SpeckleClient: + global CLIENT + CLIENT = SpeckleClient(host=HOST) + CLIENT.authenticate(token=token) def receive_data( @@ -47,6 +39,7 @@ def receive_data( return res + def get_all_meshes(child: Base): meshes = [] @@ -57,10 +50,11 @@ def get_all_meshes(child: Base): if isinstance(prop, Brep): if not hasattr(prop, "displayMesh"): break - meshes.append((prop.displayMesh, prop.id, prop.applicationId, prop)) + meshes.append((prop.displayMesh, prop.id, + prop.applicationId, prop)) elif isinstance(prop, Mesh): meshes.append((prop, prop.id, prop.applicationId)) - elif isinstance(prop, list): + elif isinstance(prop, list): for p in prop: if isinstance(p, Brep): if not hasattr(p, "displayMesh"): @@ -101,36 +95,45 @@ 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) - +def check_existing_commits() -> Any: + return None + 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}": + if commit.message == f"{_COMMIT_CURRENT}-{_COMMIT_PREV}": + print(f"Returning existing diff: {commit.id}") return commit.id return None def compare_meshes(stream_id: str, commit_current: str, commit_previous: str): - client = get_authenticated_client() + """ + Compares the meshes from the first commit against the second, and sends the result to the `diff` branch. + It returns the commit url of the diff. + """ + # Set the global variables + global _STREAM_ID, _COMMIT_CURRENT, _COMMIT_PREV + _STREAM_ID = stream_id + _COMMIT_CURRENT = commit_current + _COMMIT_PREV = commit_previous # see if existing diff commit already exists # 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) + + existing_commit = check_existing_commits() if existing_commit is not None: - url = URL + stream_id + '/commits/' + existing_commit + url = f"{HOST}/streams/{_STREAM_ID}/commits/{existing_commit}" return url + print("Did not find existing diff, processing commits now....") # get meshes from commits - previous_commit = receive_data(client, stream_id, commit_previous) + previous_commit = receive_data(CLIENT, _STREAM_ID, commit_previous) previous_meshes = get_all_meshes(previous_commit) - current_commit = receive_data(client, stream_id, commit_current) + current_commit = receive_data(CLIENT, _STREAM_ID, commit_current) 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 @@ -138,7 +141,7 @@ def compare_meshes(stream_id: str, commit_current: str, commit_previous: str): matched_current_indices = [] matched_previous_indices = [] paired_current_indices = [] - paired_previous_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]: @@ -169,7 +172,7 @@ def compare_meshes(stream_id: str, commit_current: str, commit_previous: str): same_meshes = [] ref_meshes = [] diff_mesh_pairs = [] - diff_mesh_ref_indices = [] # the corresponding ref pair mesh to 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] @@ -189,16 +192,19 @@ def compare_meshes(stream_id: str, commit_current: str, commit_previous: str): 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]]) + 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: if is_paired: - diff_values.append(find_closest_point(vertex, paired_mesh_points)) + 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) @@ -206,20 +212,19 @@ def compare_meshes(stream_id: str, commit_current: str, commit_previous: str): if diff_values[i] == 0: continue else: - index = floor(diff_values[i] / bin_size) + index = math.floor(diff_values[i] / bin_size) 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 + + 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) - else: # set colors and add mesh to diff list or paired diff list + 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) @@ -230,7 +235,8 @@ def compare_meshes(stream_id: str, commit_current: str, commit_previous: str): # 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 + # skip matched reference meshes and paired refs + if matched_previous_indices.__contains__(j) or diff_mesh_ref_indices.__contains__(j): continue mesh = previous_meshes[j][0] mesh.renderMaterial = ghosted @@ -241,23 +247,29 @@ def compare_meshes(stream_id: str, commit_current: str, commit_previous: str): 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 - return send_diff_data(stream_id, commit_current, commit_previous, units, diff_meshes, diff_mesh_pairs, diff_mesh_refs, same_meshes, ref_meshes) + return send_diff_data(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, units: str, changed: List[Mesh], changed_pairs: List[Mesh], ref_pairs: List[Base], unchanged: List[Mesh], ref: List[Mesh]): - client = get_authenticated_client() - +def send_diff_data( + units: str, + changed: List[Mesh], + changed_pairs: List[Mesh], + ref_pairs: List[Base], + unchanged: List[Mesh], + ref: List[Mesh] +): + """Sends the data resulting from the diff operation to the 'diff' branch""" # create a branch if necessary - branches = client.branch.list(stream_id) + 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" + 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" @@ -272,19 +284,16 @@ def send_diff_data(stream_id: str, commit_current: str, commit_previous: str, un base["same"] = unchanged base["ref"] = ref - transport = ServerTransport(client=client, stream_id=stream_id) + transport = ServerTransport(client=CLIENT, stream_id=_STREAM_ID) - hash = operations.send(base=base, transports=[transport]) + object_id = operations.send(base=base, transports=[transport]) - commit_id = client.commit.create( - stream_id, - hash, # object id + commit_id = CLIENT.commit.create( + _STREAM_ID, + object_id, # object id DIFF_BRANCH, - message=commit_current + "-" + commit_previous + message=_COMMIT_CURRENT + "-" + _COMMIT_PREV ) - - return URL + stream_id + '/commits/' + commit_id - -# uncomment for debug -# compare_meshes(STREAM_ID, COMMIT_ID, PREV_COMMIT_ID) - + diff_url = f"{HOST}/streams/{_STREAM_ID}/commits/{commit_id}" + print(f"Data sent to {diff_url}") + return diff_url diff --git a/server/test_mesh_diff.py b/server/test_mesh_diff.py new file mode 100644 index 0000000..359d449 --- /dev/null +++ b/server/test_mesh_diff.py @@ -0,0 +1,11 @@ +import mesh_diff as md +from specklepy.api.credentials import get_default_account + +STREAM_ID = "8325294b8f" +COMMIT_ID = "c207299871" # current commit +PREV_COMMIT_ID = "b9f376d75d" # previous commit + +account = get_default_account() +md.authenticate(account.token) +# uncomment for debug +md.compare_meshes(STREAM_ID, COMMIT_ID, PREV_COMMIT_ID) From fcec358540e812ac9755edfc833d16dd28898d27 Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Fri, 29 Oct 2021 18:35:13 +0200 Subject: [PATCH 02/12] Mesh diff is now a full fledged python class --- server/mesh_diff.py | 519 ++++++++++++++++++++------------------- server/test_mesh_diff.py | 7 +- 2 files changed, 267 insertions(+), 259 deletions(-) diff --git a/server/mesh_diff.py b/server/mesh_diff.py index 3729a6f..8b82b86 100644 --- a/server/mesh_diff.py +++ b/server/mesh_diff.py @@ -1,3 +1,5 @@ +"""SpeckleMeshDiff for AEC Tech Masterclass""" + from typing import Any, List import math from specklepy.api import operations @@ -8,292 +10,299 @@ from specklepy.objects.geometry import Brep, Point, Mesh from specklepy.objects import Base from specklepy.objects.other import RenderMaterial -HOST = "https://speckle.xyz" -DIFF_BRANCH = "diff" COLORS = [-6426, -13108, -19790, -26215, - 32640, -39322, -45747, -52429, -59111, -65536] WHITE = -1 -CLIENT: SpeckleClient = None - -_STREAM_ID: str = None -_COMMIT_CURRENT: str = None -_COMMIT_PREV: str = None -def authenticate(token: str) -> SpeckleClient: - global CLIENT - CLIENT = SpeckleClient(host=HOST) - CLIENT.authenticate(token=token) +class SpeckleMeshDiff: + """Class to handle diffing between commits in a stream.""" + client: SpeckleClient = None + host: str = None + diff_branch: str = None -def receive_data( - client: SpeckleClient, stream_id: str, commit_id: str -) -> Any: - transport = ServerTransport(client, stream_id) + commit_prev: str = None + commit_current: str = None + stream_id: str = None - commit = client.commit.get(stream_id, commit_id) - res = operations.receive(commit.referencedObject, transport) + def __init__(self, token: str, host: str = "https://speckle.xyz", diff_branch: str = "diff"): + self.host = host + self.diff_branch = diff_branch + self.client = SpeckleClient(host=self.host) + self.client.authenticate(token=token) - # if grasshopper, will be nested under data: res["data"] - # if rhino/autocad/revit, will be sent with layers or categories + def process_diff(self, stream_id: str, commit_current: str, commit_previous: str): + """ + Process a diff operation between the specified + 'current' commit and the 'previous' one. + """ + # Set the global variables + self.stream_id = stream_id + self.commit_current = commit_current + self.commit_prev = commit_previous - return res + # see if existing diff commit already exists + # query for latest x commits in diff branch + # read commit message & parse + # return url if found + existing_diff_commit = self.check_existing_commits() + if existing_diff_commit is not None: + url = f"{self.host}/streams/{self.stream_id}/commits/{existing_diff_commit}" + print(f"Returning existing diff: {url}") + return url + print("Did not find existing diff, processing commits now....") + diff_base = self.compare_meshes() + print("Diffing was successfull, sending to Speckle") + res = self.send_diff_data(diff_base) + print("Successfully sent data to Speckle") + return res -def get_all_meshes(child: Base): - meshes = [] + def receive_data(self, stream_id: str, commit_id: str) -> Base: + """Get the data from a commit on the Speckle server""" + transport = ServerTransport(self.client, stream_id) - names = child.get_dynamic_member_names() - for name in names: - prop = child[name] - if isinstance(prop, Base): - if isinstance(prop, Brep): - if not hasattr(prop, "displayMesh"): + commit = self.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 check_existing_commits(self) -> Any: + """Checks if a specific diff commit already exists in the diff_branch""" + branch_commits: Branch = self.client.branch.get( + self.stream_id, self.diff_branch, 50) + + for commit in branch_commits.commits.items: + if commit.message == f"{self.commit_current}-{self.commit_prev}": + return commit.id + + return None + + def compare_meshes(self): + """ + Compares the meshes from the first commit against the second, and sends the result to the `diff` branch. + It returns the commit url of the diff. + """ + + # get meshes from commits + previous_commit = self.receive_data(self.stream_id, self.commit_prev) + previous_meshes = self.get_all_meshes(previous_commit) + + current_commit = self.receive_data(self.stream_id, self.commit_current) + current_meshes = self.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 - 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, Brep): - if not hasattr(p, "displayMesh"): - break - 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]): - 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: + # 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) or paired_previous_indices.__contains__(i): continue - smallest_distance = d - return smallest_distance + previous_meshes_ref_pool.append(previous_meshes[i][0]) + ref_pool = self.get_all_points(previous_meshes_ref_pool) + # create a ghosted render material + ghosted = RenderMaterial() + ghosted.diffuse = WHITE + ghosted.opacity = 0.1 -def check_existing_commits() -> Any: - return None - branch_commits: Branch = CLIENT.branch.get(_STREAM_ID, DIFF_BRANCH, 50) + # for each mesh in the current commit, compare mesh vertices with ref pool or matched pair to determine scale of change + diff_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] - for commit in branch_commits.commits.items: - if commit.message == f"{_COMMIT_CURRENT}-{_COMMIT_PREV}": - print(f"Returning existing diff: {commit.id}") - return commit.id - - return None - - -def compare_meshes(stream_id: str, commit_current: str, commit_previous: str): - """ - Compares the meshes from the first commit against the second, and sends the result to the `diff` branch. - It returns the commit url of the diff. - """ - # Set the global variables - global _STREAM_ID, _COMMIT_CURRENT, _COMMIT_PREV - _STREAM_ID = stream_id - _COMMIT_CURRENT = commit_current - _COMMIT_PREV = commit_previous - - # see if existing diff commit already exists - # query for latest x commits in diff branch - # read commit message & parse - # return url if found - - existing_commit = check_existing_commits() - if existing_commit is not None: - url = f"{HOST}/streams/{_STREAM_ID}/commits/{existing_commit}" - return url - - print("Did not find existing diff, processing commits now....") - # get meshes from commits - previous_commit = receive_data(CLIENT, _STREAM_ID, commit_previous) - previous_meshes = get_all_meshes(previous_commit) - current_commit = receive_data(CLIENT, _STREAM_ID, commit_current) - 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 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) or paired_previous_indices.__contains__(i): - continue - 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 mesh vertices with ref pool or matched pair to determine scale of change - diff_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 - 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: - 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: + # send matched current meshes with rendermaterial semi-transparent (ghosted) + if matched_current_indices.__contains__(i): + mesh.renderMaterial = ghosted + same_meshes.append(mesh) continue - else: - index = math.floor(diff_values[i] / bin_size) - 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 + diff_mesh = mesh + vertices = self.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 = self.get_all_points( + [previous_meshes[paired_ref_mesh_index][0]]) + is_paired = True + + for vertex in vertices: + if is_paired: + diff_values.append(self.find_closest_point( + vertex, paired_mesh_points)) + else: + diff_values.append( + self.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: + continue + else: + index = math.floor(diff_values[i] / bin_size) + 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) + + 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) + + # process reference meshes + diff_mesh_refs = [] + for j in range(0, len(previous_meshes)): + # skip matched reference meshes and paired refs + if matched_previous_indices.__contains__(j) or diff_mesh_ref_indices.__contains__(j): + continue + mesh = previous_meshes[j][0] mesh.renderMaterial = ghosted - if is_paired: - matched_previous_indices.append(paired_ref_mesh_index) - same_meshes.append(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) + 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_meshes.append(diff_mesh) + diff_mesh_refs.append(mesh[0]) - # process reference meshes - diff_mesh_refs = [] - for j in range(0, len(previous_meshes)): - # skip matched reference meshes and paired refs - if matched_previous_indices.__contains__(j) or diff_mesh_ref_indices.__contains__(j): - continue - mesh = previous_meshes[j][0] - mesh.renderMaterial = ghosted - 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]) + # Construct diff base object to return + base = Base() + base.units = current_meshes[0][0].units + base["changed"] = diff_meshes + for i in range(0, len(diff_mesh_pairs), 1): + layer = f"changed::{i}" + base[f"{layer}::changed"] = [diff_mesh_pairs[i]] + base[f"{layer}::ref"] = [diff_mesh_refs[i]] + base["same"] = same_meshes + base["ref"] = ref_meshes + return base - # get units from first mesh in current commit - units = current_meshes[0][0].units + def send_diff_data(self, + diff_object: Base + ): + """Sends the data resulting from the diff operation to the 'diff' branch""" + # create a branch if necessary + branches = self.client.branch.list(self.stream_id) + has_res_branch = any(b.name == self.diff_branch for b in branches) + if not has_res_branch: + self.client.branch.create( + self.stream_id, name=self.diff_branch, description="all your stream diff results" + ) - # create a new commit with the diff meshes - return send_diff_data(units, diff_meshes, diff_mesh_pairs, diff_mesh_refs, same_meshes, ref_meshes) + transport = ServerTransport( + client=self.client, stream_id=self.stream_id) + object_id = operations.send(base=diff_object, transports=[transport]) -def send_diff_data( - units: str, - changed: List[Mesh], - changed_pairs: List[Mesh], - ref_pairs: List[Base], - unchanged: List[Mesh], - ref: List[Mesh] -): - """Sends the data resulting from the diff operation to the 'diff' branch""" - # 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" + commit_id = self.client.commit.create( + self.stream_id, + object_id, # object id + self.diff_branch, + message=self.commit_current + "-" + self.commit_prev ) + diff_url = f"{self.host}/streams/{self.stream_id}/commits/{commit_id}" + return diff_url - # create a commit with message "current_commit_id - previous_commit_id" - base = Base() - base.units = units - base["changed"] = changed + @staticmethod + def get_all_meshes(child: Base) -> List[Mesh]: + """Returns all the meshes from a given Base object.""" + meshes = [] - 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"] = ref + names = child.get_dynamic_member_names() + for name in names: + prop = child[name] + if isinstance(prop, Base): + if isinstance(prop, Brep): + if not hasattr(prop, "displayMesh"): + break + 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, Brep): + if not hasattr(p, "displayMesh"): + break + meshes.append( + (p.displayMesh, p.id, p.applicationId, p)) + elif isinstance(p, Mesh): + meshes.append((p, p.id, p.applicationId)) + return meshes - transport = ServerTransport(client=CLIENT, stream_id=_STREAM_ID) + @staticmethod + def get_all_points(meshes: List[Mesh]): + """Returns a flat list of vertices of all the meshes in a list""" + 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 - object_id = operations.send(base=base, transports=[transport]) + @staticmethod + def find_point(current: Point, points: List[Point]): + """Attempts to find a specific point in a list. Returns True if successful""" + for point in points: + if (point.x == current.x and point.y == current.y and point.z == current.z): + return True + return False - commit_id = CLIENT.commit.create( - _STREAM_ID, - object_id, # object id - DIFF_BRANCH, - message=_COMMIT_CURRENT + "-" + _COMMIT_PREV - ) - diff_url = f"{HOST}/streams/{_STREAM_ID}/commits/{commit_id}" - print(f"Data sent to {diff_url}") - return diff_url + @staticmethod + def find_closest_point(current: Point, points: List[Point]): + """Find the closest point to a target given a list of points""" + 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 diff --git a/server/test_mesh_diff.py b/server/test_mesh_diff.py index 359d449..e7ac4cb 100644 --- a/server/test_mesh_diff.py +++ b/server/test_mesh_diff.py @@ -1,4 +1,4 @@ -import mesh_diff as md +from mesh_diff import SpeckleMeshDiff from specklepy.api.credentials import get_default_account STREAM_ID = "8325294b8f" @@ -6,6 +6,5 @@ COMMIT_ID = "c207299871" # current commit PREV_COMMIT_ID = "b9f376d75d" # previous commit account = get_default_account() -md.authenticate(account.token) -# uncomment for debug -md.compare_meshes(STREAM_ID, COMMIT_ID, PREV_COMMIT_ID) +md = SpeckleMeshDiff(account.token) +md.process_diff(STREAM_ID, COMMIT_ID, PREV_COMMIT_ID) From 34061fd12a81f30bd0e69b76c50285157d4540c3 Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Fri, 29 Oct 2021 22:10:03 +0200 Subject: [PATCH 03/12] Minor changes to mesh diff --- server/mesh_diff.py | 98 +++++++++++++++++++++++++-------------------- 1 file changed, 55 insertions(+), 43 deletions(-) diff --git a/server/mesh_diff.py b/server/mesh_diff.py index 8b82b86..a54a1db 100644 --- a/server/mesh_diff.py +++ b/server/mesh_diff.py @@ -46,32 +46,38 @@ class SpeckleMeshDiff: # query for latest x commits in diff branch # read commit message & parse # return url if found - existing_diff_commit = self.check_existing_commits() + existing_diff_commit = None if existing_diff_commit is not None: url = f"{self.host}/streams/{self.stream_id}/commits/{existing_diff_commit}" print(f"Returning existing diff: {url}") return url - print("Did not find existing diff, processing commits now....") - diff_base = self.compare_meshes() + print("Did not find existing diff, fetching commits now....") + # get meshes from commits + previous_commit = self.receive_data( + self.client, self.stream_id, self.commit_prev) + previous_meshes = self.get_all_meshes(previous_commit) + + current_commit = self.receive_data( + self.client, self.stream_id, self.commit_current) + current_meshes = self.get_all_meshes(current_commit) + + print("Comparing meshes...") + diff_base = self.compare_meshes(current_meshes, previous_meshes) + print("Diffing was successfull, sending to Speckle") - res = self.send_diff_data(diff_base) + diff_commit_id = self.send_data( + self.client, + self.stream_id, + self.diff_branch, + diff_base, + self.commit_current + "-" + self.commit_prev) + print("Successfully sent data to Speckle") - return res + diff_url = f"{self.host}/streams/{self.stream_id}/commits/{diff_commit_id}" + return diff_url - def receive_data(self, stream_id: str, commit_id: str) -> Base: - """Get the data from a commit on the Speckle server""" - transport = ServerTransport(self.client, stream_id) - - commit = self.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 check_existing_commits(self) -> Any: + def check_existing_commits(self) -> bool or None: """Checks if a specific diff commit already exists in the diff_branch""" branch_commits: Branch = self.client.branch.get( self.stream_id, self.diff_branch, 50) @@ -82,19 +88,12 @@ class SpeckleMeshDiff: return None - def compare_meshes(self): + def compare_meshes(self, current_meshes: List[Mesh], previous_meshes: List[Mesh]) -> Base: """ Compares the meshes from the first commit against the second, and sends the result to the `diff` branch. It returns the commit url of the diff. """ - # get meshes from commits - previous_commit = self.receive_data(self.stream_id, self.commit_prev) - previous_meshes = self.get_all_meshes(previous_commit) - - current_commit = self.receive_data(self.stream_id, self.commit_current) - current_meshes = self.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 = [] @@ -220,31 +219,44 @@ class SpeckleMeshDiff: base["ref"] = ref_meshes return base - def send_diff_data(self, - diff_object: Base - ): - """Sends the data resulting from the diff operation to the 'diff' branch""" + @staticmethod + def send_data(client: SpeckleClient, stream_id: str, branch: str, diff_object: Base, message: str) -> str: + """Sends a Base object to a specified branch""" # create a branch if necessary - branches = self.client.branch.list(self.stream_id) - has_res_branch = any(b.name == self.diff_branch for b in branches) + branches = client.branch.list(stream_id) + has_res_branch = any(b.name == branch for b in branches) + if not has_res_branch: - self.client.branch.create( - self.stream_id, name=self.diff_branch, description="all your stream diff results" + client.branch.create( + stream_id, name=branch, description="This branch was created by the AEC Tech Masterclass App" ) transport = ServerTransport( - client=self.client, stream_id=self.stream_id) + client=client, stream_id=stream_id) object_id = operations.send(base=diff_object, transports=[transport]) - commit_id = self.client.commit.create( - self.stream_id, + commit_id = client.commit.create( + stream_id, object_id, # object id - self.diff_branch, - message=self.commit_current + "-" + self.commit_prev + branch, + message ) - diff_url = f"{self.host}/streams/{self.stream_id}/commits/{commit_id}" - return diff_url + + return commit_id + + @staticmethod + def receive_data(client: SpeckleClient, stream_id: str, commit_id: str) -> Base: + """Get the data from a commit on the Speckle server""" + 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 @staticmethod def get_all_meshes(child: Base) -> List[Mesh]: @@ -274,7 +286,7 @@ class SpeckleMeshDiff: return meshes @staticmethod - def get_all_points(meshes: List[Mesh]): + def get_all_points(meshes: List[Mesh]) -> List[Point]: """Returns a flat list of vertices of all the meshes in a list""" points = [] for mesh in meshes: @@ -287,7 +299,7 @@ class SpeckleMeshDiff: return points @staticmethod - def find_point(current: Point, points: List[Point]): + def find_point(current: Point, points: List[Point]) -> bool: """Attempts to find a specific point in a list. Returns True if successful""" for point in points: if (point.x == current.x and point.y == current.y and point.z == current.z): From c03e9d8262a67e28620e204eef91d797632e7b50 Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Sun, 31 Oct 2021 20:59:49 +0100 Subject: [PATCH 04/12] Updated backend to return commit instead of url --- server/main.py | 47 ++++++++++++++++++++++++++++++++++++++++----- server/mesh_diff.py | 32 ++++++++++++++++-------------- 2 files changed, 60 insertions(+), 19 deletions(-) diff --git a/server/main.py b/server/main.py index 30b2e3c..f8f9d19 100644 --- a/server/main.py +++ b/server/main.py @@ -1,8 +1,8 @@ """FastAPI Backend for the AEC Tech Masterclass""" -from fastapi import FastAPI, Request +from fastapi import FastAPI, Request, HTTPException from fastapi.middleware.cors import CORSMiddleware -import mesh_diff as md +from mesh_diff import SpeckleMeshDiff app = FastAPI() @@ -23,6 +23,43 @@ app.add_middleware( @app.get("/diff/{stream_id}/{commit_current}/{commit_previous}") def get_diff(stream_id: str, commit_current: str, commit_previous: str, request: Request): """Diffing endpoint""" - token = request.headers.get("Authorisation").split(" ")[1] - md.authenticate(token) - return md.compare_meshes(stream_id, commit_current, commit_previous) + auth_header = request.headers.get("Authorisation") + + if auth_header is None: + raise HTTPException(405, "No token provided") + + token = auth_header.split(" ")[1] + + try: + mesh_differ = SpeckleMeshDiff(token, "https://latest.speckle.dev") + diff_commit = mesh_differ.process_diff( + stream_id, commit_current, commit_previous) + except Exception as e: + raise HTTPException(500, str(e)) + return {"commit": diff_commit} + + +@app.get("/diff_check/{stream_id}/{commit_current}/{commit_previous}") +def get_diff_check(stream_id: str, commit_current: str, commit_previous: str, request: Request): + """Diffing endpoint""" + auth_header = request.headers.get("Authorisation") + + if auth_header is None: + raise HTTPException(405, "No token provided") + + token = auth_header.split(" ")[1] + + try: + mesh_differ = SpeckleMeshDiff(token, "https://latest.speckle.dev") + mesh_differ.stream_id = stream_id + mesh_differ.commit_current = commit_current + mesh_differ.commit_prev = commit_previous + existing_diff_commit = mesh_differ.check_existing_commits() + if existing_diff_commit is not None: + + return {"exists": True, "commit": existing_diff_commit} + else: + return {"exists": False, "commit": None} + + except Exception as e: + raise HTTPException(500, str(e)) diff --git a/server/mesh_diff.py b/server/mesh_diff.py index a54a1db..12977f1 100644 --- a/server/mesh_diff.py +++ b/server/mesh_diff.py @@ -1,6 +1,6 @@ """SpeckleMeshDiff for AEC Tech Masterclass""" -from typing import Any, List +from typing import List import math from specklepy.api import operations from specklepy.api.client import SpeckleClient @@ -42,16 +42,6 @@ class SpeckleMeshDiff: self.commit_current = commit_current self.commit_prev = commit_previous - # see if existing diff commit already exists - # query for latest x commits in diff branch - # read commit message & parse - # return url if found - existing_diff_commit = None - if existing_diff_commit is not None: - url = f"{self.host}/streams/{self.stream_id}/commits/{existing_diff_commit}" - print(f"Returning existing diff: {url}") - return url - print("Did not find existing diff, fetching commits now....") # get meshes from commits previous_commit = self.receive_data( @@ -74,8 +64,9 @@ class SpeckleMeshDiff: self.commit_current + "-" + self.commit_prev) print("Successfully sent data to Speckle") - diff_url = f"{self.host}/streams/{self.stream_id}/commits/{diff_commit_id}" - return diff_url + return self.client.commit.get(self.stream_id, diff_commit_id) + # diff_url = f"{self.host}/streams/{self.stream_id}/commits/{diff_commit_id}" + # return diff_url def check_existing_commits(self) -> bool or None: """Checks if a specific diff commit already exists in the diff_branch""" @@ -84,7 +75,7 @@ class SpeckleMeshDiff: for commit in branch_commits.commits.items: if commit.message == f"{self.commit_current}-{self.commit_prev}": - return commit.id + return commit return None @@ -283,6 +274,19 @@ class SpeckleMeshDiff: (p.displayMesh, p.id, p.applicationId, p)) elif isinstance(p, Mesh): meshes.append((p, p.id, p.applicationId)) + elif isinstance(p, list): + for subp in p: + if isinstance(subp, Brep): + if not hasattr(subp, "displayMesh"): + break + meshes.append( + (subp.displayMesh, subp.id, subp.applicationId, subp)) + elif isinstance(subp, Mesh): + meshes.append( + (subp, subp.id, subp.applicationId)) + elif isinstance(subp, Base): + meshes += SpeckleMeshDiff.get_all_meshes(subp) + return meshes @staticmethod From 0fb161f550f165e440ac29e29b4792a376f04010 Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Sun, 31 Oct 2021 21:00:12 +0100 Subject: [PATCH 05/12] Frontend improvements --- .../components/commitSelector/CommitPanel.vue | 296 +++++++++++------- frontend/src/components/viewer/Renderer.vue | 26 +- frontend/src/speckleQueries.js | 28 +- frontend/src/views/StreamView.vue | 57 ++-- 4 files changed, 246 insertions(+), 161 deletions(-) diff --git a/frontend/src/components/commitSelector/CommitPanel.vue b/frontend/src/components/commitSelector/CommitPanel.vue index 7bc1a5d..904310b 100644 --- a/frontend/src/components/commitSelector/CommitPanel.vue +++ b/frontend/src/components/commitSelector/CommitPanel.vue @@ -1,137 +1,207 @@ - + + diff --git a/frontend/src/components/viewer/Renderer.vue b/frontend/src/components/viewer/Renderer.vue index 11ad6d8..0d350eb 100644 --- a/frontend/src/components/viewer/Renderer.vue +++ b/frontend/src/components/viewer/Renderer.vue @@ -213,7 +213,6 @@ import throttle from "lodash.throttle" import { Viewer } from "@speckle/viewer" import { TOKEN } from "@/speckleUtils" import ObjectSimpleViewer from "@/components/viewer/ObjectSimpleViewer" -import { debounce } from "debounce" export default { components: { ObjectSimpleViewer }, @@ -237,10 +236,6 @@ export default { embeded: { type: Boolean, default: false - }, - controls: { - type: Object, - default: null } }, data() { @@ -283,8 +278,8 @@ export default { } }, objectUrl(newVal, oldVal) { - console.log("obj urls changed", newVal, oldVal) if (newVal == oldVal) return + console.log("obj url changed", newVal, oldVal) this.unloadData() this.load() } @@ -296,9 +291,7 @@ export default { if (!this.viewer) { this.viewer = new Viewer({ container: this.$refs.renderer }) } - this.$emit("update:controls", this.viewer.controls) this.viewer.onWindowResize() - console.log("viewer", this.viewer) this.setupEvents() }, beforeDestroy() { @@ -347,6 +340,7 @@ export default { }, load() { if (!this.objectUrl) return + this.viewer.loadObject(this.objectUrl, localStorage.getItem(TOKEN)) this.viewerLastLoadedUrl = this.objectUrl diff --git a/frontend/src/speckleQueries.js b/frontend/src/speckleQueries.js index 78bc865..a0fd58d 100644 --- a/frontend/src/speckleQueries.js +++ b/frontend/src/speckleQueries.js @@ -29,6 +29,7 @@ export const streamCommitsQuery = ` sourceApplication referencedObject authorName + authorAvatar createdAt } } diff --git a/frontend/src/views/StreamView.vue b/frontend/src/views/StreamView.vue index 88a04ce..2adf7a0 100644 --- a/frontend/src/views/StreamView.vue +++ b/frontend/src/views/StreamView.vue @@ -13,14 +13,12 @@ @@ -29,7 +27,6 @@ @@ -39,7 +36,7 @@ @@ -51,4 +62,4 @@ export default { .v-data-footer__select { display: none !important; } - \ No newline at end of file + diff --git a/vetur.config.js b/vetur.config.js new file mode 100644 index 0000000..28aa672 --- /dev/null +++ b/vetur.config.js @@ -0,0 +1,34 @@ +// vetur.config.js +/** @type {import('vls').VeturConfig} */ +module.exports = { + // **optional** default: `{}` + // override vscode settings + // Notice: It only affects the settings used by Vetur. + settings: { + "vetur.useWorkspaceDependencies": true, + "vetur.experimental.templateInterpolationService": true + }, + // **optional** default: `[{ root: './' }]` + // support monorepos + projects: [ + "./packages/frontend", // Shorthand for specifying only the project root location + { + // **required** + // Where is your project? + // It is relative to `vetur.config.js`. + root: "./packages/frontend", + // **optional** default: `'package.json'` + // Where is `package.json` in the project? + // We use it to determine the version of vue. + // It is relative to root property. + package: "./package.json", + + // **optional** default: `[]` + // Register globally Vue component glob. + // If you set it, you can get completion by that components. + // It is relative to root property. + // Notice: It won't actually do it. You need to use `require.context` or `Vue.component` + globalComponents: ["./src/components/**/*.vue"] + } + ] +} From faac4ee8216a830adb5de42e7865432949941be5 Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Tue, 2 Nov 2021 14:59:52 +0100 Subject: [PATCH 09/12] Initial readme file --- README.md | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f3f21a3..d78bf9a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,94 @@ -# speckle-aectech-masterclass -web app for comparing Speckle stream commits +

+
+ Speckle | AEC Tech Masterclass +

+

+ A Speckle app to compare different commits and visualize the results +

+

Speckle is data infrastructure for the AEC industry.


+ +

Twitter Follow Community forum users website docs

+ +# About Speckle + +What is Speckle? Check our ![YouTube Video Views](https://img.shields.io/youtube/views/B9humiSpHzM?label=Speckle%20in%201%20minute%20video&style=social) + +### Features + +- **Object-based:** say goodbye to files! Speckle is the first object based platform for the AEC industry +- **Version control:** Speckle is the Git & Hub for geometry and BIM data +- **Collaboration:** share your designs collaborate with others +- **3D Viewer:** see your CAD and BIM models online, share and embed them anywhere +- **Interoperability:** get your CAD and BIM models into other software without exporting or importing +- **Real time:** get real time updates and notifications and changes +- **GraphQL API:** get what you need anywhere you want it +- **Webhooks:** the base for a automation and next-gen pipelines +- **Built for developers:** we are building Speckle with developers in mind and got tools for every stack +- **Built for the AEC industry:** Speckle connectors are plugins for the most common software used in the industry such as Revit, Rhino, Grasshopper, AutoCAD, Civil 3D, Excel, Unreal Engine, Unity, QGIS, Blender and more! + +### Try Speckle now! + +Give Speckle a try in no time by: + +- [![speckle XYZ](https://img.shields.io/badge/https://-speckle.xyz-0069ff?style=flat-square&logo=hackthebox&logoColor=white)](https://speckle.xyz) ⇒ creating an account at +- [![create a droplet](https://img.shields.io/badge/Create%20a%20Droplet-0069ff?style=flat-square&logo=digitalocean&logoColor=white)](https://marketplace.digitalocean.com/apps/speckle-server?refcode=947a2b5d7dc1) ⇒ deploying an instance in 1 click + +### Resources + +- [![Community forum users](https://img.shields.io/badge/community-forum-green?style=for-the-badge&logo=discourse&logoColor=white)](https://speckle.community) for help, feature requests or just to hang with other speckle enthusiasts, check out our community forum! +- [![website](https://img.shields.io/badge/tutorials-speckle.systems-royalblue?style=for-the-badge&logo=youtube)](https://speckle.systems) our tutorials portal is full of resources to get you started using Speckle +- [![docs](https://img.shields.io/badge/docs-speckle.guide-orange?style=for-the-badge&logo=read-the-docs&logoColor=white)](https://speckle.guide/dev/) reference on almost any end-user and developer functionality + +![Untitled](https://user-images.githubusercontent.com/2679513/132021739-15140299-624d-4410-98dc-b6ae6d9027ab.png) + +# Repo structure + +This repo is divided into two distinct parts, a **frontend** and a **server** + +## Frontend + +The `frontend/` folder contains all code related to the app any user will be able to access. + +The app was built using `Vue.js`, and communicates with Speckle through our `GraphQL API`. It also communicates with the app's `server` using `REST` calls. + +## Backend + +The `server/` folder contains all code related to the app's backend. + +It's a [FastAPI](https://link) server, written in Python and uses our Python SDK to communicate with our [public server](https://speckle.xyz) + +> `FastAPI` generates automatic documentation on the `/docs` route of the server. + +It is a very basic server composed of two routes: + +## `diff-check/STREAM_ID/CURRENT_COMMIT_ID/PREV_COMMIT_ID` + +Checks for an existing commit in a predefined `diff` branch and returns the _diff commit_ if it does exist. + +## `diff/STREAM_ID/CURRENT_COMMIT_ID/PREV_COMMIT_ID` + +Performs a diff operation against two commits from the same stream. + +Result of the `diff` operation will be commited to the stream on a predefined branch. + +Returns the diff commit if the operation was successfull. + +# Workshop pre-requisites + +For the **server**, you must have: + +- `python` installed in your computer (at least version `3.6`, `3.9` recommended) [Download it here](https://www.python.org/downloads/) +- Install `FastAPI`: + ```shell + pip install fastapi + # and then + pip install "uvicorn[standard]" + ``` +- + +For the **Vue app**: + +- Install `node` [Download it here](https://nodejs.org/en/download/) +- Install `vue CLI` - [Instructions here](https://cli.vuejs.org/guide/installation.html) +- Install `vetur` VSCode extension +- Install `vue dev tools` for Chrome \ No newline at end of file From 92a1788364eba9f72b22eb3266918ea2d35a0806 Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Wed, 3 Nov 2021 16:56:41 +0100 Subject: [PATCH 10/12] cleanup: Removed unused query --- frontend/src/speckleQueries.js | 11 ----------- frontend/src/speckleUtils.js | 2 -- 2 files changed, 13 deletions(-) diff --git a/frontend/src/speckleQueries.js b/frontend/src/speckleQueries.js index a0fd58d..1e7b4b0 100644 --- a/frontend/src/speckleQueries.js +++ b/frontend/src/speckleQueries.js @@ -50,17 +50,6 @@ export const streamSearchQuery = ` } }` -export const streamObjectQuery = `query($streamId: String!, $objectId: String!) { - stream(id: $streamId){ - object(id: $objectId){ - totalChildrenCount - id - speckleType - data - } - } -}` - export const latestStreamsQuery = `query { streams(limit: 10){ cursor diff --git a/frontend/src/speckleUtils.js b/frontend/src/speckleUtils.js index e2a528b..906ad98 100644 --- a/frontend/src/speckleUtils.js +++ b/frontend/src/speckleUtils.js @@ -88,8 +88,6 @@ export const searchStreams = (e) => speckleFetch(streamSearchQuery, {searchText: // Get commits related to a specific stream, allows for pagination by passing a cursor export const getStreamCommits = (streamId, itemsPerPage, cursor) => speckleFetch(streamCommitsQuery, {id: streamId, cursor, limit: itemsPerPage}) -export const getStreamObject = (streamId, objectId) => speckleFetch(streamObjectQuery, {streamId, objectId}).then(res => res.data?.stream?.object?.data) - export const getObject = (streamId, objectId) => speckleFetch(streamObjectQuery, {streamId, objectId}) export const getStreams = () => speckleFetch(latestStreamsQuery).then(res => res.data?.streams) From 2d9f179c1464d784543353642e538b0c2eb71324 Mon Sep 17 00:00:00 2001 From: Alan Rynne Date: Wed, 3 Nov 2021 18:29:56 +0100 Subject: [PATCH 11/12] Mesh diff fix for revit elements :) --- frontend/src/views/StreamView.vue | 2 +- server/mesh_diff.py | 14 ++------------ 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/frontend/src/views/StreamView.vue b/frontend/src/views/StreamView.vue index 2adf7a0..dec511e 100644 --- a/frontend/src/views/StreamView.vue +++ b/frontend/src/views/StreamView.vue @@ -1,5 +1,5 @@