Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c00635d093 | |||
| a07d4f0a8e | |||
| 61e721716f | |||
| 91d12b5a6c | |||
| f331846138 | |||
| d350887860 | |||
| 8ad607a8e0 | |||
| 13f242e47f | |||
| 9f55acd02d | |||
| 476f947b68 | |||
| f56dea0a35 | |||
| c8d6b3ebea | |||
| b2e7a899f6 | |||
| ef0f7ef46f | |||
| 8799f52dfb | |||
| 22c1fb4fb2 | |||
| bce03f9e38 | |||
| dc1d1adefc | |||
| 4de1f6ecc1 | |||
| 4eaea0cc51 | |||
| ccf2996096 | |||
| aa0a4d2f3b | |||
| 48ef9420de | |||
| 6c223dba33 |
@@ -1,5 +1,5 @@
|
||||
# SpeckleBlender 2.0
|
||||
Speckle add-on for Blender 2.92
|
||||
Speckle add-on for Blender 2.92 & 2.93
|
||||
|
||||
[](https://twitter.com/SpeckleSystems) [](https://discourse.speckle.works) [](https://speckle.systems) [](https://speckle.guide/dev/)
|
||||
|
||||
@@ -45,7 +45,7 @@ This code is WIP and as such should be used with extreme caution on non-sensitiv
|
||||
## Custom properties
|
||||
|
||||
- **SpeckleBlender** will look for a `texture_coordinates` property and use that to create a UV layer for the imported object. These texture coordinates are a space-separated list of floats (`[u v u v u v etc...]`) that is encoded as a base64 blob. This is subject to change as **SpeckleBlender** develops.
|
||||
- If a `material` property is found, **SpeckleBlender** will create a material named using the sub-property `material.name`. If a material with that name already exists in Blender, **SpeckleBlender** will just assign that existing material to the object. This allows geometry to be updated without having to re-assign and re-create materials.
|
||||
- If a `renderMaterial` property is found, **SpeckleBlender** will create a material named using the sub-property `renderMaterial.name`. If a material with that name already exists in Blender, **SpeckleBlender** will just assign that existing material to the object. This allows geometry to be updated without having to re-assign and re-create materials.
|
||||
- Vertex colors are supported. The `colors` list from Speckle meshes is translated to a vertex color layer.
|
||||
- Speckle properties will be imported as custom properties on Blender objects. Nested dictionaries are expanded to individual properties by flattening their key hierarchy. I.e. `propA:{'propB': {'propC':10, 'propD':'foobar'}}` is flattened to `propA.propB.propC = 10` and `propA.propB.propD = "foobar"`.
|
||||
|
||||
|
||||
@@ -4,12 +4,12 @@ from mathutils import Matrix
|
||||
from .from_speckle import *
|
||||
from .to_speckle import *
|
||||
from .util import *
|
||||
from bpy_speckle.util import find_key_case_insensitive
|
||||
from bpy_speckle.functions import _report
|
||||
from bpy_speckle.functions import _report, get_scale_length
|
||||
|
||||
from specklepy.objects.geometry import *
|
||||
from specklepy.objects.other import RenderMaterial
|
||||
|
||||
|
||||
FROM_SPECKLE_SCHEMAS = {
|
||||
Mesh: import_mesh,
|
||||
Brep: import_brep,
|
||||
@@ -21,17 +21,6 @@ FROM_SPECKLE_SCHEMAS = {
|
||||
}
|
||||
|
||||
|
||||
# FROM_SPECKLE = {
|
||||
# "Mesh": import_mesh,
|
||||
# "Brep": import_brep,
|
||||
# "Curve": import_curve,
|
||||
# "Line": import_curve,
|
||||
# "Polyline": import_curve,
|
||||
# "Polycurve":import_curve,
|
||||
# "Arc":import_curve,
|
||||
# }
|
||||
|
||||
|
||||
TO_SPECKLE = {
|
||||
"MESH": export_mesh,
|
||||
"CURVE": export_curve,
|
||||
@@ -60,13 +49,14 @@ def add_blender_material(smesh, blender_object) -> None:
|
||||
if blender_object.data is None:
|
||||
return
|
||||
|
||||
if not hasattr(smesh, "renderMaterial"):
|
||||
if not hasattr(smesh, "renderMaterial") and not hasattr(smesh, "@renderMaterial"):
|
||||
return
|
||||
|
||||
speckle_mat = smesh.renderMaterial
|
||||
mat_name = getattr(speckle_mat, "name", None)
|
||||
speckle_mat = getattr(smesh, "renderMaterial", None) or smesh["@renderMaterial"]
|
||||
mat_name = getattr(speckle_mat, "name", None) or speckle_mat.__dict__.get("@name")
|
||||
if not mat_name:
|
||||
mat_name = speckle_mat.applicationId or speckle_mat.id
|
||||
|
||||
blender_mat = bpy.data.materials.get(mat_name)
|
||||
if not blender_mat:
|
||||
blender_mat = bpy.data.materials.new(mat_name)
|
||||
@@ -146,7 +136,10 @@ def add_custom_properties(speckle_object, blender_object):
|
||||
blender_object["_speckle_type"] = type(speckle_object).__name__
|
||||
# blender_object['_speckle_name'] = "SpeckleObject"
|
||||
|
||||
ignore = ["_chunkable", "_units"]
|
||||
ignore = ["_chunkable", "_units", "units"]
|
||||
|
||||
if hasattr(speckle_object, "applicationId"):
|
||||
blender_object["applicationId"] = speckle_object.applicationId
|
||||
|
||||
for key in speckle_object.get_dynamic_member_names():
|
||||
if key in ignore:
|
||||
@@ -180,21 +173,32 @@ def dict_to_speckle_object(data):
|
||||
|
||||
|
||||
def from_speckle_object(speckle_object, scale, name=None):
|
||||
speckle_name = (
|
||||
name
|
||||
or getattr(speckle_object, "name", None)
|
||||
or speckle_object.speckle_type + f" -- {speckle_object.id}"
|
||||
)
|
||||
|
||||
units = getattr(speckle_object, "units", None)
|
||||
if units:
|
||||
scale = get_scale_length(units) / bpy.context.scene.unit_settings.scale_length
|
||||
|
||||
# try native conversion
|
||||
if type(speckle_object) in FROM_SPECKLE_SCHEMAS.keys():
|
||||
print("Got object type: {}".format(type(speckle_object)))
|
||||
speckle_name = (
|
||||
name
|
||||
or getattr(speckle_object, "name", None)
|
||||
or speckle_object.speckle_type + f" -- {speckle_object.id}"
|
||||
)
|
||||
|
||||
obdata = FROM_SPECKLE_SCHEMAS[type(speckle_object)](
|
||||
speckle_object, scale, speckle_name
|
||||
)
|
||||
try:
|
||||
obdata = FROM_SPECKLE_SCHEMAS[type(speckle_object)](
|
||||
speckle_object, scale, speckle_name
|
||||
)
|
||||
except Exception as e: # conversion error
|
||||
_report(f"Error converting {speckle_object} \n{e}")
|
||||
return None
|
||||
|
||||
if speckle_name in bpy.data.objects.keys():
|
||||
blender_object = bpy.data.objects[speckle_name]
|
||||
blender_object.data = obdata
|
||||
blender_object.matrix_world = Matrix()
|
||||
if hasattr(obdata, "materials"):
|
||||
blender_object.data.materials.clear()
|
||||
else:
|
||||
@@ -210,9 +214,16 @@ def from_speckle_object(speckle_object, scale, name=None):
|
||||
|
||||
return blender_object
|
||||
|
||||
else:
|
||||
_report("Invalid input: {}".format(speckle_object))
|
||||
return None
|
||||
# try display mesh
|
||||
mesh = getattr(
|
||||
speckle_object, "displayMesh", getattr(speckle_object, "displayValue", None)
|
||||
)
|
||||
if mesh:
|
||||
return from_speckle_object(mesh, scale, speckle_name)
|
||||
|
||||
# return none if fail
|
||||
_report("Invalid input: {}".format(speckle_object))
|
||||
return None
|
||||
|
||||
|
||||
def get_speckle_subobjects(attr, scale, name):
|
||||
@@ -265,12 +276,12 @@ def get_blender_custom_properties(obj, max_depth=1000):
|
||||
return obj
|
||||
|
||||
if hasattr(obj, "keys"):
|
||||
d = {}
|
||||
for key in obj.keys():
|
||||
if key in ignored_keys or key.startswith("_"):
|
||||
continue
|
||||
d[key] = get_blender_custom_properties(obj[key], max_depth - 1)
|
||||
return d
|
||||
return {
|
||||
key: get_blender_custom_properties(obj[key], max_depth - 1)
|
||||
for key in obj.keys()
|
||||
if key not in ignored_keys and not key.startswith("_")
|
||||
}
|
||||
|
||||
elif isinstance(obj, (list, tuple, idprop.types.IDPropertyArray)):
|
||||
return [get_blender_custom_properties(o, max_depth - 1) for o in obj]
|
||||
else:
|
||||
@@ -289,6 +300,7 @@ def to_speckle_object(blender_object, scale):
|
||||
|
||||
for so in speckle_objects:
|
||||
so.properties = get_blender_custom_properties(blender_object)
|
||||
so.applicationId = so.properties.pop("applicationId", None)
|
||||
|
||||
if speckle_material:
|
||||
so["renderMaterial"] = speckle_material
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import bpy, math
|
||||
from bpy_speckle.util import find_key_case_insensitive
|
||||
from mathutils import Vector, Quaternion
|
||||
import mathutils
|
||||
from specklepy.objects.geometry import *
|
||||
|
||||
CONVERT = {}
|
||||
@@ -85,7 +85,7 @@ def import_nurbs_curve(scurve, bcurve, scale):
|
||||
1,
|
||||
)
|
||||
|
||||
if len(scurve.weights == len(nurbs.points)):
|
||||
if len(scurve.weights) == len(nurbs.points):
|
||||
for i, w in enumerate(scurve.weights):
|
||||
nurbs.points[i].weight = w
|
||||
|
||||
@@ -109,7 +109,7 @@ def import_arc(rcurve, bcurve, scale):
|
||||
return
|
||||
|
||||
origin = plane.origin
|
||||
normal = Vector(plane.normal.value)
|
||||
normal = mathutils.Vector([plane.normal.x, plane.normal.y, plane.normal.z])
|
||||
|
||||
xaxis = plane.xdir
|
||||
yaxis = plane.ydir
|
||||
@@ -118,20 +118,20 @@ def import_arc(rcurve, bcurve, scale):
|
||||
startAngle = rcurve.startAngle
|
||||
endAngle = rcurve.endAngle
|
||||
|
||||
startQuat = Quaternion(normal, startAngle)
|
||||
endQuat = Quaternion(normal, endAngle)
|
||||
startQuat = mathutils.Quaternion(normal, startAngle)
|
||||
endQuat = mathutils.Quaternion(normal, endAngle)
|
||||
|
||||
"""
|
||||
Get start and end vectors, centre point, angles, etc.
|
||||
"""
|
||||
|
||||
r1 = Vector(plane.xdir.value)
|
||||
r1 = mathutils.Vector([plane.xdir.x, plane.xdir.y, plane.xdir.z])
|
||||
r1.rotate(startQuat)
|
||||
|
||||
r2 = Vector(plane.xdir.value)
|
||||
r2 = mathutils.Vector([plane.xdir.x, plane.xdir.y, plane.xdir.z])
|
||||
r2.rotate(endQuat)
|
||||
|
||||
c = Vector(plane.origin.value) * scale
|
||||
c = mathutils.Vector([plane.origin.x, plane.origin.y, plane.origin.z]) * scale
|
||||
|
||||
spt = c + r1 * radius
|
||||
ept = c + r2 * radius
|
||||
@@ -149,7 +149,7 @@ def import_arc(rcurve, bcurve, scale):
|
||||
|
||||
Ndiv = max(int(math.floor(angle / 0.3)), 2)
|
||||
step = angle / float(Ndiv)
|
||||
stepQuat = Quaternion(normal, step)
|
||||
stepQuat = mathutils.Quaternion(normal, step)
|
||||
tan = math.tan(step / 2) * radius
|
||||
|
||||
arc.points.add(Ndiv + 1)
|
||||
|
||||
@@ -157,7 +157,7 @@ def import_mesh(speckle_mesh, scale=1.0, name=None):
|
||||
if not name:
|
||||
name = speckle_mesh.geometryHash or speckle_mesh.id
|
||||
|
||||
if name in bpy.data.meshes.keys() and False:
|
||||
if name in bpy.data.meshes.keys():
|
||||
mesh = bpy.data.meshes[name]
|
||||
else:
|
||||
mesh = bpy.data.meshes.new(name=name)
|
||||
|
||||
@@ -2,8 +2,100 @@ import bpy, bmesh, struct
|
||||
from specklepy.objects.geometry import Curve, Interval, Box, Polyline
|
||||
from bpy_speckle.convert.to_speckle.mesh import export_mesh
|
||||
|
||||
UNITS = "m"
|
||||
|
||||
|
||||
def bezier_to_speckle(matrix, spline, scale, name=None):
|
||||
degree = 3
|
||||
closed = spline.use_cyclic_u
|
||||
|
||||
points = []
|
||||
for i, bp in enumerate(spline.bezier_points):
|
||||
if i > 0:
|
||||
points.append(tuple(matrix @ bp.handle_left * scale))
|
||||
points.append(tuple(matrix @ bp.co * scale))
|
||||
if i < len(spline.bezier_points) - 1:
|
||||
points.append(tuple(matrix @ bp.handle_right * scale))
|
||||
|
||||
if closed:
|
||||
points.append(tuple(matrix @ spline.bezier_points[-1].handle_right * scale))
|
||||
points.append(tuple(matrix @ spline.bezier_points[0].handle_left * scale))
|
||||
points.append(tuple(matrix @ spline.bezier_points[0].co * scale))
|
||||
|
||||
num_points = len(points)
|
||||
|
||||
knot_count = num_points + degree - 1
|
||||
knots = [0] * knot_count
|
||||
|
||||
for i in range(1, len(knots)):
|
||||
knots[i] = i // 3
|
||||
|
||||
length = spline.calc_length()
|
||||
domain = Interval(start=0, end=length, totalChildrenCount=0)
|
||||
return Curve(
|
||||
name=name,
|
||||
degree=degree,
|
||||
closed=spline.use_cyclic_u,
|
||||
periodic=spline.use_cyclic_u,
|
||||
points=list(sum(points, ())), # magic (flatten list of tuples)
|
||||
weights=[1] * num_points,
|
||||
knots=knots,
|
||||
rational=False,
|
||||
area=0,
|
||||
volume=0,
|
||||
length=length,
|
||||
domain=domain,
|
||||
units=UNITS,
|
||||
bbox=Box(area=0.0, volume=0.0),
|
||||
)
|
||||
|
||||
|
||||
def nurbs_to_speckle(matrix, spline, scale, name=None):
|
||||
knots = makeknots(spline)
|
||||
# print("knots: {}".format(knots))
|
||||
points = [tuple(matrix @ pt.co.xyz * scale) for pt in spline.points]
|
||||
degree = spline.order_u - 1
|
||||
|
||||
length = spline.calc_length()
|
||||
domain = Interval(start=0, end=length, totalChildrenCount=0)
|
||||
|
||||
return Curve(
|
||||
name=name,
|
||||
degree=degree,
|
||||
closed=spline.use_cyclic_u,
|
||||
periodic=spline.use_cyclic_u,
|
||||
points=list(sum(points, ())), # magic (flatten list of tuples)
|
||||
weights=[pt.weight for pt in spline.points],
|
||||
knots=knots,
|
||||
rational=False,
|
||||
area=0,
|
||||
volume=0,
|
||||
length=length,
|
||||
domain=domain,
|
||||
units=UNITS,
|
||||
bbox=Box(area=0.0, volume=0.0),
|
||||
)
|
||||
|
||||
|
||||
def poly_to_speckle(matrix, spline, scale, name=None):
|
||||
points = [tuple(matrix @ pt.co.xyz * scale) for pt in spline.points]
|
||||
|
||||
length = spline.calc_length()
|
||||
domain = Interval(start=0, end=length, totalChildrenCount=0)
|
||||
return Polyline(
|
||||
name=name,
|
||||
closed=spline.use_cyclic_u,
|
||||
value=list(sum(points, ())), # magic (flatten list of tuples)
|
||||
length=length,
|
||||
domain=domain,
|
||||
bbox=Box(area=0.0, volume=0.0),
|
||||
area=0,
|
||||
units=UNITS,
|
||||
)
|
||||
|
||||
|
||||
def export_curve(blender_object, data, scale=1.0):
|
||||
UNITS = "m" if bpy.context.scene.unit_settings.system == "METRIC" else "ft"
|
||||
|
||||
if blender_object.type != "CURVE":
|
||||
return None
|
||||
@@ -18,115 +110,22 @@ def export_curve(blender_object, data, scale=1.0):
|
||||
mesh = export_mesh(blender_object, blender_object.to_mesh(), scale)
|
||||
curves.extend(mesh)
|
||||
|
||||
unit_system = bpy.context.scene.unit_settings.system
|
||||
|
||||
for spline in data.splines:
|
||||
if spline.type == "BEZIER":
|
||||
|
||||
degree = 3
|
||||
closed = spline.use_cyclic_u
|
||||
|
||||
points = []
|
||||
for i, bp in enumerate(spline.bezier_points):
|
||||
if i > 0:
|
||||
points.append(tuple(mat @ bp.handle_left * scale))
|
||||
points.append(tuple(mat @ bp.co * scale))
|
||||
if i < len(spline.bezier_points) - 1:
|
||||
points.append(tuple(mat @ bp.handle_right * scale))
|
||||
|
||||
if closed:
|
||||
points.append(
|
||||
tuple(mat @ spline.bezier_points[-1].handle_right * scale)
|
||||
)
|
||||
points.append(tuple(mat @ spline.bezier_points[0].handle_left * scale))
|
||||
points.append(tuple(mat @ spline.bezier_points[0].co * scale))
|
||||
|
||||
num_points = len(points)
|
||||
|
||||
knot_count = num_points + degree - 1
|
||||
knots = [0] * knot_count
|
||||
|
||||
for i in range(1, len(knots), 1):
|
||||
knots[i] = i // 3
|
||||
|
||||
length = spline.calc_length()
|
||||
domain = Interval(
|
||||
start=0, end=length, totalChildrenCount=0, applicationId="Blender"
|
||||
)
|
||||
bezier = Curve(
|
||||
degree=degree,
|
||||
closed=spline.use_cyclic_u,
|
||||
periodic=spline.use_cyclic_u,
|
||||
points=list(sum(points, ())), # magic (flatten list of tuples)
|
||||
weights=[1] * num_points,
|
||||
knots=knots,
|
||||
rational=False,
|
||||
area=0,
|
||||
volume=0,
|
||||
length=length,
|
||||
domain=domain,
|
||||
units="m" if unit_system == "METRIC" else "ft",
|
||||
bbox=Box(area=0.0, volume=0.0),
|
||||
applicationId="Blender",
|
||||
)
|
||||
|
||||
curves.append(bezier)
|
||||
curves.append(bezier_to_speckle(mat, spline, scale, blender_object.name))
|
||||
|
||||
elif spline.type == "NURBS":
|
||||
|
||||
knots = makeknots(spline)
|
||||
# print("knots: {}".format(knots))
|
||||
points = [tuple(mat @ pt.co.xyz * scale) for pt in spline.points]
|
||||
degree = spline.order_u - 1
|
||||
|
||||
length = spline.calc_length()
|
||||
domain = Interval(
|
||||
start=0, end=length, totalChildrenCount=0, applicationId="Blender"
|
||||
)
|
||||
nurbs = Curve(
|
||||
name=blender_object.name,
|
||||
degree=degree,
|
||||
closed=spline.use_cyclic_u,
|
||||
periodic=spline.use_cyclic_u,
|
||||
points=list(sum(points, ())), # magic (flatten list of tuples)
|
||||
weights=[pt.weight for pt in spline.points],
|
||||
knots=knots,
|
||||
rational=False,
|
||||
area=0,
|
||||
volume=0,
|
||||
length=length,
|
||||
domain=domain,
|
||||
units="m" if unit_system == "METRIC" else "ft",
|
||||
bbox=Box(area=0.0, volume=0.0),
|
||||
applicationId="Blender",
|
||||
)
|
||||
|
||||
curves.append(nurbs)
|
||||
curves.append(nurbs_to_speckle(mat, spline, scale, blender_object.name))
|
||||
|
||||
elif spline.type == "POLY":
|
||||
points = [tuple(mat @ pt.co.xyz * scale) for pt in spline.points]
|
||||
|
||||
length = spline.calc_length()
|
||||
domain = Interval(
|
||||
start=0, end=length, totalChildrenCount=0, applicationId="Blender"
|
||||
)
|
||||
poly = Polyline(
|
||||
name=blender_object.name,
|
||||
closed=spline.use_cyclic_u,
|
||||
value=list(sum(points, ())), # magic (flatten list of tuples)
|
||||
length=length,
|
||||
domain=domain,
|
||||
bbox=Box(area=0.0, volume=0.0),
|
||||
area=0,
|
||||
units="m" if unit_system == "METRIC" else "ft",
|
||||
applicationId="Blender",
|
||||
)
|
||||
curves.append(poly)
|
||||
curves.append(poly_to_speckle(mat, spline, scale, blender_object.name))
|
||||
|
||||
return curves
|
||||
|
||||
|
||||
def export_ngons_as_polylines(blender_object, data, scale=1.0):
|
||||
UNITS = "m" if bpy.context.scene.unit_settings.system == "METRIC" else "ft"
|
||||
|
||||
if blender_object.type != "MESH":
|
||||
return None
|
||||
|
||||
@@ -139,7 +138,7 @@ def export_ngons_as_polylines(blender_object, data, scale=1.0):
|
||||
for v in poly.vertices:
|
||||
value.extend(mat @ verts[v].co * scale)
|
||||
|
||||
domain = Interval(start=0, end=1, applicationId="Blender")
|
||||
domain = Interval(start=0, end=1)
|
||||
poly = Polyline(
|
||||
name="{}_{}".format(blender_object.name, i),
|
||||
closed=True,
|
||||
@@ -148,8 +147,7 @@ def export_ngons_as_polylines(blender_object, data, scale=1.0):
|
||||
domain=domain,
|
||||
bbox=Box(area=0.0, volume=0.0),
|
||||
area=0,
|
||||
units="m" if unit_system == "METRIC" else "ft",
|
||||
applicationId="Blender",
|
||||
units=UNITS,
|
||||
)
|
||||
|
||||
polylines.append(poly)
|
||||
|
||||
@@ -26,7 +26,6 @@ def export_mesh(blender_object, data, scale=1.0):
|
||||
colors=[],
|
||||
units="m" if unit_system == "METRIC" else "ft",
|
||||
bbox=Box(area=0.0, volume=0.0),
|
||||
applicationId="Blender",
|
||||
)
|
||||
|
||||
for f in faces:
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
from specklepy.api.client import SpeckleClient
|
||||
import requests
|
||||
|
||||
from bpy_speckle.clients import speckle_clients
|
||||
|
||||
"""
|
||||
|
||||
@@ -110,7 +110,7 @@ class DeleteObject(bpy.types.Operator):
|
||||
for x in res["resource"]["objects"]
|
||||
if x["_id"] == active.speckle.object_id
|
||||
]
|
||||
if existing == None:
|
||||
if existing is None:
|
||||
return {"CANCELLED"}
|
||||
# print("Existing: %s" % SpeckleResource.to_json_pretty(existing))
|
||||
new_objects = [
|
||||
@@ -177,7 +177,7 @@ class UploadNgonsAsPolylines(bpy.types.Operator):
|
||||
res = client.objects.create([polyline])
|
||||
print(res)
|
||||
|
||||
if res == None:
|
||||
if res is None:
|
||||
_report(client.me)
|
||||
continue
|
||||
placeholders.extend(res)
|
||||
@@ -185,7 +185,7 @@ class UploadNgonsAsPolylines(bpy.types.Operator):
|
||||
# polyline['_id'] = res['_id']
|
||||
# placeholders.append({'type':'Placeholder', '_id':res['_id']})
|
||||
|
||||
if len(placeholders) < 1:
|
||||
if not placeholders:
|
||||
return {"CANCELLED"}
|
||||
|
||||
# Get list of existing objects in stream and append new object to list
|
||||
@@ -247,7 +247,7 @@ class UploadObject(bpy.types.Operator):
|
||||
sm = to_speckle_object(active, scale)
|
||||
|
||||
placeholders = client.objects.create([sm])
|
||||
if placeholders == None:
|
||||
if placeholders is None:
|
||||
return {"CANCELLED"}
|
||||
|
||||
sstream = client.streams.get(stream.id)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
"""
|
||||
Stream operators
|
||||
"""
|
||||
|
||||
from itertools import chain
|
||||
from typing import Dict
|
||||
import bpy, bmesh, os
|
||||
import webbrowser
|
||||
from bpy.props import (
|
||||
@@ -31,13 +32,14 @@ from specklepy.objects import Base
|
||||
from specklepy.objects.geometry import *
|
||||
|
||||
|
||||
def get_objects_collections(base):
|
||||
def get_objects_collections(base) -> Dict:
|
||||
"""Create collections based on the dynamic members on a root commit object"""
|
||||
collections = {}
|
||||
for name in base.get_dynamic_member_names():
|
||||
value = base[name]
|
||||
if isinstance(value, list):
|
||||
col = create_collection(name)
|
||||
collections[name] = [item for item in value if isinstance(item, Base)]
|
||||
collections[name] = get_objects_nested_lists(value, col)
|
||||
if isinstance(value, Base):
|
||||
col = create_collection(name)
|
||||
collections[name] = get_objects_collections_recursive(value, col)
|
||||
@@ -45,8 +47,37 @@ def get_objects_collections(base):
|
||||
return collections
|
||||
|
||||
|
||||
def get_objects_collections_recursive(base, parent_col=None):
|
||||
def get_objects_nested_lists(items, parent_col=None) -> List:
|
||||
"""For handling the weird nested lists that come from Grasshopper"""
|
||||
objects = []
|
||||
|
||||
if isinstance(items[0], list):
|
||||
items = list(chain.from_iterable(items))
|
||||
objects.extend(get_objects_nested_lists(items, parent_col))
|
||||
else:
|
||||
objects = [
|
||||
get_objects_collections_recursive(item, parent_col)
|
||||
for item in items
|
||||
if isinstance(item, Base)
|
||||
]
|
||||
|
||||
return objects
|
||||
|
||||
|
||||
def get_objects_collections_recursive(base, parent_col=None) -> List:
|
||||
"""Recursively create collections based on the dynamic members on nested `Base` objects within the root commit object"""
|
||||
# if it's a convertable (registered) class and not just a plain `Base`, return the object itself
|
||||
object_type = Base.get_registered_type(base.speckle_type)
|
||||
if (
|
||||
(object_type and object_type != Base)
|
||||
or hasattr(base, "displayMesh")
|
||||
or hasattr(base, "displayValue")
|
||||
):
|
||||
return [base]
|
||||
|
||||
# if it's an unknown type, try to drill further down to find convertable objects
|
||||
objects = []
|
||||
|
||||
for name in base.get_dynamic_member_names():
|
||||
value = base[name]
|
||||
if isinstance(value, list):
|
||||
@@ -54,8 +85,10 @@ def get_objects_collections_recursive(base, parent_col=None):
|
||||
if isinstance(item, Base):
|
||||
objects.append(item)
|
||||
if isinstance(value, Base):
|
||||
col = create_collection(name)
|
||||
parent_col.children.link(col)
|
||||
col = parent_col.children.get(name)
|
||||
if not parent_col.children.get(name):
|
||||
col = create_collection(name)
|
||||
parent_col.children.link(col)
|
||||
objects.append({name: get_objects_collections_recursive(value, col)})
|
||||
|
||||
return objects
|
||||
@@ -70,60 +103,71 @@ def bases_to_native(context, collections, scale, stream_id, func=None):
|
||||
elif isinstance(objects, list):
|
||||
for obj in objects:
|
||||
if isinstance(obj, dict):
|
||||
bases_to_native(context, obj, scale, stream_id)
|
||||
bases_to_native(context, obj, scale, stream_id, func)
|
||||
elif isinstance(obj, list):
|
||||
for item in obj:
|
||||
if isinstance(item, dict):
|
||||
bases_to_native(context, item, scale, stream_id, func)
|
||||
elif isinstance(item, Base):
|
||||
base_to_native(
|
||||
context, item, scale, stream_id, col, existing, func
|
||||
)
|
||||
elif isinstance(obj, Base):
|
||||
base_to_native(context, obj, scale, stream_id, col, existing, func)
|
||||
else:
|
||||
new_objects = [from_speckle_object(obj, scale)]
|
||||
_report(
|
||||
f"Something went wrong when receiving collection: {col_name}"
|
||||
)
|
||||
|
||||
if hasattr(obj, "properties") and obj.properties is not None:
|
||||
new_objects.extend(
|
||||
get_speckle_subobjects(obj.properties, scale, obj.id)
|
||||
)
|
||||
elif isinstance(obj, dict) and "properties" in obj.keys():
|
||||
new_objects.extend(
|
||||
get_speckle_subobjects(obj["properties"], scale, obj["id"])
|
||||
)
|
||||
|
||||
"""
|
||||
Set object Speckle settings
|
||||
"""
|
||||
for new_object in new_objects:
|
||||
if new_object is None:
|
||||
continue
|
||||
|
||||
"""
|
||||
Run injected function
|
||||
"""
|
||||
if func:
|
||||
new_object = func(context.scene, new_object)
|
||||
|
||||
if (
|
||||
new_object is None
|
||||
): # Make sure that the injected function returned an object
|
||||
new_obj = new_object
|
||||
_report(
|
||||
"Script '{}' returned None.".format(func.__module__)
|
||||
)
|
||||
continue
|
||||
|
||||
new_object.speckle.stream_id = stream_id
|
||||
new_object.speckle.send_or_receive = "receive"
|
||||
|
||||
if new_object.speckle.object_id in existing.keys():
|
||||
name = existing[new_object.speckle.object_id].name
|
||||
existing[new_object.speckle.object_id].name = (
|
||||
name + "__deleted"
|
||||
)
|
||||
new_object.name = name
|
||||
col.objects.unlink(existing[new_object.speckle.object_id])
|
||||
|
||||
if new_object.name not in col.objects:
|
||||
col.objects.link(new_object)
|
||||
bpy.context.view_layer.update()
|
||||
|
||||
if context.area:
|
||||
context.area.tag_redraw()
|
||||
|
||||
|
||||
def base_to_native(context, base, scale, stream_id, col, existing, func=None):
|
||||
new_objects = [from_speckle_object(base, scale)]
|
||||
|
||||
if hasattr(base, "properties") and base.properties is not None:
|
||||
new_objects.extend(get_speckle_subobjects(base.properties, scale, base.id))
|
||||
elif isinstance(base, dict) and "properties" in base.keys():
|
||||
new_objects.extend(
|
||||
get_speckle_subobjects(base["properties"], scale, base["id"])
|
||||
)
|
||||
|
||||
"""
|
||||
Set object Speckle settings
|
||||
"""
|
||||
for new_object in new_objects:
|
||||
if new_object is None:
|
||||
continue
|
||||
|
||||
"""
|
||||
Run injected function
|
||||
"""
|
||||
if func:
|
||||
new_object = func(context.scene, new_object)
|
||||
|
||||
if (
|
||||
new_object is None
|
||||
): # Make sure that the injected function returned an object
|
||||
new_obj = new_object
|
||||
_report("Script '{}' returned None.".format(func.__module__))
|
||||
continue
|
||||
|
||||
new_object.speckle.stream_id = stream_id
|
||||
new_object.speckle.send_or_receive = "receive"
|
||||
|
||||
if new_object.speckle.object_id in existing.keys():
|
||||
name = existing[new_object.speckle.object_id].name
|
||||
existing[new_object.speckle.object_id].name = name + "__deleted"
|
||||
new_object.name = name
|
||||
col.objects.unlink(existing[new_object.speckle.object_id])
|
||||
|
||||
if new_object.name not in col.objects:
|
||||
col.objects.link(new_object)
|
||||
|
||||
|
||||
def create_collection(name, clear_collection=True):
|
||||
if name in bpy.data.collections:
|
||||
col = bpy.data.collections[name]
|
||||
@@ -234,7 +278,6 @@ class ReceiveStreamObjects(bpy.types.Operator):
|
||||
return {"CANCELLED"}
|
||||
|
||||
name = "{} [ {} @ {} ]".format(stream.name, branch.name, commit.id)
|
||||
|
||||
col = create_collection(name)
|
||||
col.speckle.stream_id = stream.id
|
||||
col.speckle.name = stream.name
|
||||
@@ -243,8 +286,10 @@ class ReceiveStreamObjects(bpy.types.Operator):
|
||||
bpy.context.scene.collection.children.link(col)
|
||||
|
||||
for child_col in collections.keys():
|
||||
col.children.link(bpy.data.collections[child_col])
|
||||
|
||||
try:
|
||||
col.children.link(bpy.data.collections[child_col])
|
||||
except:
|
||||
pass
|
||||
"""
|
||||
Set conversion scale from stream units
|
||||
"""
|
||||
@@ -380,12 +425,16 @@ class SendStreamObjects(bpy.types.Operator):
|
||||
|
||||
transport = ServerTransport(client, stream.id)
|
||||
|
||||
obj_id = operations.send(base, [transport])
|
||||
obj_id = operations.send(
|
||||
base,
|
||||
[transport],
|
||||
)
|
||||
client.commit.create(
|
||||
stream.id,
|
||||
obj_id,
|
||||
branch.name,
|
||||
message=self.commit_message,
|
||||
source_application="blender",
|
||||
)
|
||||
|
||||
bpy.ops.speckle.load_user_streams()
|
||||
|
||||
@@ -55,6 +55,8 @@ class LoadUsers(bpy.types.Operator):
|
||||
except Exception as ex:
|
||||
_report(ex)
|
||||
users.remove(len(users) - 1)
|
||||
if profile.isDefault:
|
||||
context.scene.speckle.active_user = str(len(users) - 1)
|
||||
|
||||
context.scene.speckle.active_user_index = int(context.scene.speckle.active_user)
|
||||
bpy.ops.speckle.load_user_streams()
|
||||
@@ -83,7 +85,7 @@ class LoadUserStreams(bpy.types.Operator):
|
||||
client = speckle_clients[int(context.scene.speckle.active_user)]
|
||||
|
||||
try:
|
||||
streams = client.stream.list()
|
||||
streams = client.stream.list(stream_limit=20)
|
||||
except Exception as e:
|
||||
_report("Failed to retrieve streams: {}".format(e))
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user