Hi @baileydw,
It’s the same when I bake them. At least I don’t get any closer to where the issue is.
Below is an extract from my code which shows the method I used to get the NURBS values.
Those are then passed to Speckle and their Brep class (python code example), which on import, passes the values to Rhino’s Brep class like so: link
This is my example as GH file. It uses some Speckle components but I have also internalised just the Brep.
Brep Example.gh (127.1 KB)
C++ Methods to get the Nurbs values for the Brep.
It’s still in development, and currently, get_Surfaces
needs to be called before get_Curves2D
.
Brep::Curves2D Brep::get_Curves2D() const {
auto face_origin = this->mesh.property_map<Surface_mesh::Face_index, Kernel::Point_2>("f:origin").value();
Curves2D curves = Curves2D();
curves.resize(mesh.number_of_halfedges());
for (auto h : mesh.halfedges()){
Brep::Curve2D curve = std::vector<tg::pos2>(2);
auto v1 = mesh.source(h);
auto v2 = mesh.target(h);
auto plane = compute_plane(mesh, mesh.face(h));
// Alight geometry to surace
auto o = face_origin[mesh.face(h)];
Kernel::Vector_2 Ov = Kernel::Vector_2(o.x(), o.y());
auto pt1 = plane.to_2d(mesh.point(v1)) - Ov;
auto pt2 = plane.to_2d(mesh.point(v2)) - Ov;
curve[1] = tg::pos2(CGAL::to_double(pt1.x()), CGAL::to_double(pt1.y()));
curve[0] = tg::pos2(CGAL::to_double(pt2.x()), CGAL::to_double(pt2.y()));
curves[h.idx()] = curve;
}
return curves;
}
Brep::Curves3D Brep::get_Curves3D() const {
Curves3D curves = Curves3D();
curves.resize(mesh.number_of_edges());
for (auto e : mesh.edges())
{
auto curve = std::vector<tg::pos3>(2);
auto h = mesh.halfedge(e, 0);
auto p1 = mesh.point(mesh.source(h));
auto p2 = mesh.point(mesh.target(h));
curve[0] = tg::pos3(CGAL::to_double(p1.x()), CGAL::to_double(p1.y()), CGAL::to_double(p1.z()));
curve[1] = tg::pos3(CGAL::to_double(p2.x()), CGAL::to_double(p2.y()), CGAL::to_double(p2.z()));
curves[e.idx()] = curve;
}
return curves;
}
Brep::Edges Brep::get_Edges() const {
Edges edges = Edges();
edges.resize(mesh.num_edges());
for (auto e : mesh.edges())
{
auto edge = Brep::Edge();
edge.Curve3dIndex = e.idx();
auto h = mesh.halfedge(e);
edge.TrimIndices.resize(2);
edge.TrimIndices[0] = h.idx();
edge.TrimIndices[1] = mesh.opposite(h).idx();
auto v1 = mesh.vertex(e, 1);
auto v2 = mesh.vertex(e, 0);
auto p1 = mesh.point(v1);
auto p2 = mesh.point(v2);
edge.StartIndex = v1.idx();
edge.EndIndex = v2.idx();
edge.ProxyCurveIsReversed = false;
edge.Domain = Interval(0, tg::distance(tg::pos3(CGAL::to_double(p1.x()), CGAL::to_double(p1.y()), CGAL::to_double(p1.z())),tg::pos3(CGAL::to_double(p2.x()), CGAL::to_double(p2.y()), CGAL::to_double(p2.z()))));
edge.Curve3dIndex = e.idx();
edges[e.idx()] = edge;
}
return edges;
}
Brep::Faces Brep::get_Faces() const {
Faces faces = Faces();
faces.resize(mesh.number_of_faces());
for (auto f : mesh.faces())
{
auto face = Brep::Face();
face.SurfaceIndex = f.idx();
face.OuterLoopIndex = f.idx();
face.LoopIndices = f.idx();
faces[f.idx()] = face;
}
return faces;
}
Brep::Vertices Brep::get_Vertices() const {
Vertices vertices = Vertices();
vertices.resize(mesh.number_of_vertices());
for (auto v : mesh.vertices()){
auto p = mesh.point(v);
vertices[v.idx()] = tg::pos3(CGAL::to_double(p.x()), CGAL::to_double(p.y()), CGAL::to_double(p.z()));
}
return vertices;
}
Brep::Surfaces Brep::get_Surfaces() const {
auto surface_origin = this->mesh.property_map<Surface_mesh::Face_index, Kernel::Point_2>("f:origin").value();
Surfaces surfaces = Surfaces();
surfaces.resize(mesh.number_of_faces());
for (auto f : mesh.faces())
{
auto surface = Brep::Surface();
surface.degreeU = 1;
surface.degreeV = 1;
surface.rational = false;
surface.countU = 2;
surface.countV = 2;
surface.closedU = false;
surface.closedV = false;
surface.pointData.reserve(4/*Points*/ * 4/*Fields*/);
// Get all the points of the face
std::vector<Kernel::Point_3> points;
for (auto v : mesh.vertices_around_face(mesh.halfedge(f)))
points.push_back(mesh.point(v));
auto normal = PMP::compute_face_normal(f, mesh);
CGAL::Plane_3 plane = Kernel::Plane_3(points[0], normal);
std::vector<Kernel::Point_2> points_2d;
for (auto p: points)
points_2d.push_back(plane.to_2d(p));
auto bbox = CGAL::bounding_box(points_2d.begin(), points_2d.end());
// Offset bbox
// TODO: Find a way to calculate the correct offset.
// Some surfaces seem to require an offset
static const double offset = 0.3;
bbox = CGAL::Bbox_2(
CGAL::to_double(bbox.xmin()-offset),
CGAL::to_double(bbox.ymin()-offset),
CGAL::to_double(bbox.xmax()+offset),
CGAL::to_double(bbox.ymax()+offset));
points.clear();
points.resize(4);
points[0] = plane.to_3d(Kernel::Point_2(bbox.xmin(), bbox.ymin()));
points[1] = plane.to_3d(Kernel::Point_2(bbox.xmin(), bbox.ymax()));
points[2] = plane.to_3d(Kernel::Point_2(bbox.xmax(), bbox.ymin()));
points[3] = plane.to_3d(Kernel::Point_2(bbox.xmax(), bbox.ymax()));
for (auto & p: points)
{
surface.pointData.push_back(CGAL::to_double(p.x()));
surface.pointData.push_back(CGAL::to_double(p.y()));
surface.pointData.push_back(CGAL::to_double(p.z()));
surface.pointData.push_back(1.0);
}
surface_origin[f] = Kernel::Point_2(bbox.xmin(), bbox.ymin());
float distU = bbox.xmax()-bbox.xmin();
float distV = bbox.ymax()-bbox.ymin();
surface.domainU = Interval(0, distU);
surface.domainV = Interval(0, distV);
surface.knotsU = std::vector<float>{0, distU};
surface.knotsV = std::vector<float>{0, distV};
surfaces[f.idx()] = surface;
}
return surfaces;
}
Brep::Loops Brep::get_Loops() const {
Loops loops = Loops();
loops.resize(mesh.number_of_faces());
for (auto f : mesh.faces()){
auto loop = Brep::BrepLoop();
loop.FaceIndex = f.idx();
loop.Type = Brep::BrepLoop::BrepLoopType::Outer;
loop.TrimIndices = std::vector<int>();
for (auto h: mesh.halfedges_around_face(mesh.halfedge(f)))
loop.TrimIndices.push_back(h.idx());
std::reverse(loop.TrimIndices.begin(), loop.TrimIndices.end());
loops[f.idx()] = loop;
}
return loops;
}
Brep::Trims Brep::get_Trims() const {
Trims trims = Trims();
trims.resize(mesh.num_halfedges());
for (auto h : mesh.halfedges())
{
auto trim = Brep::BrepTrim();
// 3D curve index
trim.EdgeIndex = mesh.edge(h).idx();
trim.StartIndex = mesh.source(h).idx();
trim.EndIndex = mesh.target(h).idx();
trim.FaceIndex = mesh.face(h).idx();
trim.LoopIndex = mesh.face(h).idx();
// 2D curve index
trim.CurveIndex = h.idx();// mesh.edge(h).idx();
trim.IsoStatus = 0; //What is this? --> https://developer.rhino3d.com/api/rhinocommon/rhino.geometry.isostatus
trim.TrimType = Brep::BrepTrim::BrepTrimType::Mated;
// This value needs to be set
auto e = mesh.edge(h);
trim.IsReversed = ( mesh.vertex(e,0) == mesh.source(h) )? true : false;
auto p1 = mesh.point(mesh.source(h));
auto p2 = mesh.point(mesh.target(h));
auto dist = tg::distance(
tg::pos3(
CGAL::to_double(p1.x()),
CGAL::to_double(p1.y()),
CGAL::to_double(p1.z())),
tg::pos3(
CGAL::to_double(p2.x()),
CGAL::to_double(p2.y()),
CGAL::to_double(p2.z())));
trim.Domain = Interval(0, dist);
trims[h.idx()] = trim;
}
return trims;
}
Python code that handles the actual creation of the Speckle object.
def BrepToBase(brep: Brep ) -> Base:
sp_brep = Speckle_Brep()
sp_brep.provenance = "LinkML"
sp_brep.units = Units.m
sp_brep.bbox = AABBToBase(brep.bbox())
sp_brep.area = brep.area()
sp_brep.volume = brep.volume()
sp_brep.IsClosed = brep.is_closed()
sp_brep.Orientation = brep.Orientation()
sp_brep.displayValue = [MeshToBase(brep.get_mesh())]
sp_brep.Curve3D = [Line(
# Swap y and z because of the point cloud is in a Y-up coordinate system
start = Point(x = points[0].x, y = points[0].z, z = points[0].y),
end = Point(x = points[-1].x, y= points[-1].z, z = points[-1].y),
domain = Interval(start = 0, end = 1),
# bbox: Optional[Box] = None
# length: Optional[float] = None
) for points in brep.Curves3D()]
# Swap y and z because of the point cloud is in a Y-up coordinate system
sp_brep.Vertices = [ Point(x=p.x, y=p.z,z=p.y) for p in brep.Vertices() ]
# Surfaces be generated before the 2D curves
sp_brep.Surfaces = [
Surface(
degreeU = s.degreeU,
degreeV = s.degreeV,
rational = s.rational,
# area = s.area,
# Swap y and z because of the point cloud is in a Y-up coordinate system
countU = s.countU,
countV = s.countV,
# bbox = AABBToBase(s.bbox),
closedU = s.closedU,
closedV = s.closedV,
domainU = Interval(start = s.domainU[0], end = s.domainU[1]),
domainV = Interval(start = s.domainV[0], end = s.domainV[1]),
pointData = [ v for p in chunks(s.pointData,4) for v in [p[0], p[2], p[1], p[3]]],
knotsU = s.knotsU,
knotsV = s.knotsV
) for s in brep.Surfaces()]
# Be sure to fist construct the surfaces before the 2D curves
sp_brep.Curve2D = [ Line(
start = Point(x = points[0].x, y = points[0].y, z = 0),
end = Point(x = points[-1].x, y= points[-1].y, z = 0),
domain = Interval(start = 0, end = 1),
# bbox: Optional[Box] = None
# length: Optional[float] = None
) for points in brep.Curves2D()]
sp_brep.Edges = [BrepEdge(
Curve3dIndex = e.Curve3dIndex,
TrimIndices = e.TrimIndices,
StartIndex = e.StartIndex,
EndIndex = e.EndIndex,
ProxyCurveIsReversed = e.ProxyCurveIsReversed,
Domain = Interval(
start = e.Domain[0],
end = e.Domain[1])
) for e in brep.Edges()]
sp_brep.Loops = [
BrepLoop(
FaceIndex = l.FaceIndex,
TrimIndices = l.TrimIndices,
Type = BrepLoopType.Unknown ,#l.Type
) for l in brep.Loops()]
sp_brep.Faces = [
BrepFace(
SurfaceIndex = f.SurfaceIndex,
OuterLoopIndex = f.OuterLoopIndex,
OrientationReversed = f.OrientationReversed,
LoopIndices = [f.LoopIndices]
) for f in brep.Faces()]
sp_brep.Trims = [
BrepTrim(
EdgeIndex = t.EdgeIndex,
StartIndex = t.StartIndex,
EndIndex = t.EndIndex,
FaceIndex = t.FaceIndex,
LoopIndex = t.LoopIndex,
CurveIndex = t.CurveIndex,
IsoStatus = t.IsoStatus,
TrimType = BrepTrimType.Unknown,# t.TrimType,
IsReversed = t.IsReversed
# Domain = Interval( start = t.Domain[0], end = t.Domain[0])
) for t in brep.Trims()]
return sp_brep