def triangulate(vertices): t = Triangulator() for i, v in enumerate(vertices): t.add_vertex(v) t.add_polygon_vertex(i) t.triangulate() # Make sure that the result is consistent by starting each triangle with # the lowest index value. That makes it easier to use predetermined values # in the test cases. result = set() for n in range(t.get_num_triangles()): # Switch to lowest matching index value in case of duplicates. v0 = vertices.index(vertices[t.get_triangle_v0(n)]) v1 = vertices.index(vertices[t.get_triangle_v1(n)]) v2 = vertices.index(vertices[t.get_triangle_v2(n)]) if v1 < v0: v0, v1, v2 = v1, v2, v0 if v1 < v0: v0, v1, v2 = v1, v2, v0 result.add((v0, v1, v2)) return result
def triangulate_outer_polygon_with_hole_polygons( outerPolygonWithInnerHolePolygons): # TODO: this needs to be all changed, and support for holes needs to be # integrated tr = Triangulator() for vertex in outerPolygonWithInnerHolePolygons.outer_polygon.getVertices( ): vi = tr.addVertex(vertex[0], vertex[1]) tr.addPolygonVertex(vi) for inner_hole_polygon in outerPolygonWithInnerHolePolygons.inner_hole_polygons: tr.beginHole() # finish the last hole, and add to the next for vertex in inner_hole_polygon.getVertices(): vi = tr.addVertex(vertex[0], vertex[1]) tr.addHoleVertex(vi) # tr.beginHole() # finish the last hole? tr.triangulate() # --- BEGIN getting the triangles vertices and indices ---- def get_vertices_and_indices_from_p3d_triangulated_object(tr): # first linear array of triangle points triangles_indices = np.array([]) num_triangles = tr.getNumTriangles() for i in range(num_triangles): triangle = np.array([]) # 2d vertices triangle = np.append(triangle, tr.getTriangleV0(i)) triangle = np.append(triangle, tr.getTriangleV1(i)) triangles_indices = np.append(triangles_indices, triangle) # reshape it triangles_indices = np.reshape(triangles_indices, (-1, 2)) triangles_indices = np.array(triangles_indices, np.int32) # print(tr.isLeftWinding()) # print(tr.getNumTriangles()) # print(tr.getNumVertices()) # return all vertices and indices needed to create a GeomPrimitive vertices = tr.getVertices() return vertices, triangles_indices return get_vertices_and_indices_from_p3d_triangulated_object(tr)
def getTriangulatorGeomData(pointList, normalVec, texScale=(1, 1), color=(1, 1, 1, 1)): """ takes a list of points and a normal vector, gets the corresponding triangulated polygon, returns a dict with the data of that triangulated polygon : data = {"prims":[], "vertices" : [], "normals" : [], "texcoords" : []} """ data = { "prims": [], "vertices": [], "normals": [], "texcoords": [], "colors": [] } normalVec = Vec3(normalVec) normalVec.normalize() u, v = texScale trig = Triangulator() for x, y, z in pointList: if normalVec[2] > 0: vi = trig.addVertex(x, y) data["texcoords"].append(Point2(x * u, y * u)) elif normalVec[1] > 0: vi = trig.addVertex(x, z) data["texcoords"].append(Point2(x * u, z * u)) else: vi = trig.addVertex(y, z) data["texcoords"].append(Point2(y * u, z * u)) data["vertices"].append(Point3(x, y, z)) data["normals"].append(normalVec) data["colors"].append(color) trig.addPolygonVertex(vi) trig.triangulate() for i in xrange(trig.getNumTriangles()): A, B, C = trig.getTriangleV0(i), trig.getTriangleV1( i), trig.getTriangleV2(i) data["prims"].append((A, B, C)) #if normalVec[2]>0: # data["prims"].append((A, B, C)) #else: # data["prims"].append((A, C, B)) return data
def getTriangulatorGeomData(pointList, normalVec): """ takes a list of points and a normal vector, gets the corresponding triangulated polygon, returns a dict with the data of that triangulated polygon : data = {"prims":[], "vertices" : [], "normals" : [], "texcoords" : []} """ data = {"prims":[], "vertices" : [], "normals" : [], "texcoords" : []} #prim = GeomTriangles(Geom.UHStatic) trig = Triangulator() #vdata = GeomVertexData('trig', GeomVertexFormat.getV3n3c4t2(), Geom.UHStatic) #vwriter = GeomVertexWriter(vdata, 'vertex') #nvwriter = GeomVertexWriter(vdata, 'normal') #tvwriter = GeomVertexWriter(vdata, 'texcoord') for x, y, z in pointList: vi = trig.addVertex(x, y) #vwriter.addData3f(x, y, z) data["vertices"].append(Point3(x, y, z)) #nvwriter.addData3f(normalVec) data["normals"].append(normalVec) #tvwriter.addData2f(x,y) data["texcoords"].append(Point2(x, y)) trig.addPolygonVertex(vi) #print "added vertex vi = %s" % (vi) try: trig.triangulate() except: return None #prim = GeomTriangles(Geom.UHStatic) for i in range(trig.getNumTriangles()): A, B, C = trig.getTriangleV0(i), trig.getTriangleV1(i), trig.getTriangleV2(i) #print "triangle %s : %s, %s, %s" % (i, A, B, C) if normalVec[2]<0: #prim.addVertices(A, B, C) data["prims"].append((A, B, C)) else: #prim.addVertices(A, C, B) data["prims"].append((A, C, B)) #prim.closePrimitive() return data
def getTriangulatorGeomData(pointList, normalVec): """ takes a list of points and a normal vector, gets the corresponding triangulated polygon, returns a dict with the data of that triangulated polygon : data = {"prims":[], "vertices" : [], "normals" : [], "texcoords" : []} """ data = {"prims": [], "vertices": [], "normals": [], "texcoords": []} #prim = GeomTriangles(Geom.UHStatic) trig = Triangulator() #vdata = GeomVertexData('trig', GeomVertexFormat.getV3n3c4t2(), Geom.UHStatic) #vwriter = GeomVertexWriter(vdata, 'vertex') #nvwriter = GeomVertexWriter(vdata, 'normal') #tvwriter = GeomVertexWriter(vdata, 'texcoord') for x, y, z in pointList: vi = trig.addVertex(x, y) #vwriter.addData3f(x, y, z) data["vertices"].append(Point3(x, y, z)) #nvwriter.addData3f(normalVec) data["normals"].append(normalVec) #tvwriter.addData2f(x,y) data["texcoords"].append(Point2(x, y)) trig.addPolygonVertex(vi) #print "added vertex vi = %s" % (vi) try: trig.triangulate() except: return None #prim = GeomTriangles(Geom.UHStatic) for i in range(trig.getNumTriangles()): A, B, C = trig.getTriangleV0(i), trig.getTriangleV1( i), trig.getTriangleV2(i) #print "triangle %s : %s, %s, %s" % (i, A, B, C) if normalVec[2] < 0: #prim.addVertices(A, B, C) data["prims"].append((A, B, C)) else: #prim.addVertices(A, C, B) data["prims"].append((A, C, B)) #prim.closePrimitive() return data
def create_geom_node(self): xyzero = False i = 0 for i, x in enumerate(self.normal): if x == 1: xyzero = True break # vt=tuple(self.vertices) t = Triangulator() fmt = GeomVertexFormat.getV3cp() vdata = GeomVertexData('name', fmt, Geom.UHStatic) vertex = GeomVertexWriter(vdata, 'vertex') _ = GeomVertexWriter(vdata, 'color') for point in self._vertices: (x, y, z) = point v = (x, y) if not xyzero: v = (x, y) elif i == 0: v = (y, z) elif i == 1: v = (x, z) elif i == 2: v = (x, y) t.addPolygonVertex(t.addVertex(*v)) vertex.addData3f(x, y, z) t.triangulate() prim = GeomTriangles(Geom.UHStatic) for n in range(t.getNumTriangles()): prim.addVertices(t.getTriangleV0(n), t.getTriangleV1(n), t.getTriangleV2(n)) prim.closePrimitive() geom = Geom(vdata) geom.addPrimitive(prim) node = GeomNode('gnode') node.addGeom(geom) return node
def makeNode(self, pointmap=(lambda x, y: (x, y, 0))): vt = tuple(self.vertices) t = Triangulator() fmt = GeomVertexFormat.getV3() vdata = GeomVertexData('name', fmt, Geom.UHStatic) vertex = GeomVertexWriter(vdata, 'vertex') for x, y in vt: t.addPolygonVertex(t.addVertex(x, y)) vertex.addData3f(pointmap(x, y)) t.triangulate() prim = GeomTriangles(Geom.UHStatic) for n in xrange(t.getNumTriangles()): prim.addVertices(t.getTriangleV0(n),t.getTriangleV1(n),t.getTriangleV2(n)) prim.closePrimitive() geom = Geom(vdata) geom.addPrimitive(prim) node = GeomNode('gnode') node.addGeom(geom) return node
def reconstruct(self): trianglator = Triangulator() #Add vertices to the trianglator for vertex in self.vertices: trianglator.addPolygonVertex(trianglator.addVertex(vertex)) trianglator.triangulate() #Prepare to create the primative self.vdata = GeomVertexData('floor', GeomVertexFormat.getV3n3cpt2(), Geom.UHStatic) vertexW = GeomVertexWriter(self.vdata, 'vertex') normalW = GeomVertexWriter(self.vdata, 'normal') colorW = GeomVertexWriter(self.vdata, 'color') texcoordW = GeomVertexWriter(self.vdata, 'texcoord') #Add vertices to the primative i = 0 while i < trianglator.getNumVertices(): vertex = trianglator.getVertex(i) vertexW.addData3f(vertex.x, vertex.y, 0.0) normalW.addData3f(0, 0, 1) colorW.addData4f(0.1, 0.1, 0.1, 0.5) texcoordW.addData2f(0.0, 1.0) i += 1 self.geom = Geom(self.vdata) #Add triangles to the primative i = 0 print(trianglator.getNumTriangles()) while i < trianglator.getNumTriangles(): tri = GeomTriangles(Geom.UHStatic) tri.addVertices(trianglator.getTriangleV0(i), trianglator.getTriangleV1(i), trianglator.getTriangleV2(i)) tri.closePrimitive() self.geom.addPrimitive(tri) i += 1 self.addGeom(self.geom)
def reconstruct(self): trianglator = Triangulator() #Add vertices to the trianglator for vertex in self.vertices: trianglator.addPolygonVertex(trianglator.addVertex(vertex)) trianglator.triangulate() #Prepare to create the primative self.vdata = GeomVertexData('floor', GeomVertexFormat.getV3n3cpt2(), Geom.UHStatic) vertexW = GeomVertexWriter(self.vdata, 'vertex') normalW = GeomVertexWriter(self.vdata, 'normal') colorW = GeomVertexWriter(self.vdata, 'color') texcoordW = GeomVertexWriter(self.vdata, 'texcoord') #Add vertices to the primative i = 0 while i < trianglator.getNumVertices(): vertex = trianglator.getVertex(i) vertexW.addData3f(vertex.x,vertex.y,0.0) normalW.addData3f(0,0,1) colorW.addData4f(0.1,0.1,0.1,0.5) texcoordW.addData2f(0.0, 1.0) i+=1 self.geom = Geom(self.vdata) #Add triangles to the primative i = 0 print(trianglator.getNumTriangles()) while i < trianglator.getNumTriangles(): tri = GeomTriangles(Geom.UHStatic) tri.addVertices(trianglator.getTriangleV0(i),trianglator.getTriangleV1(i),trianglator.getTriangleV2(i)) tri.closePrimitive() self.geom.addPrimitive(tri) i+=1 self.addGeom(self.geom)
def getTriangulatorGeomData(pointList, normalVec, texScale=(1,1), color=(1,1,1,1)): """ takes a list of points and a normal vector, gets the corresponding triangulated polygon, returns a dict with the data of that triangulated polygon : data = {"prims":[], "vertices" : [], "normals" : [], "texcoords" : []} """ data = {"prims":[], "vertices" : [], "normals" : [], "texcoords" : [], "colors" : []} normalVec = Vec3(normalVec) normalVec.normalize() u, v = texScale trig = Triangulator() for x, y, z in pointList: if normalVec[2]>0: vi = trig.addVertex(x, y) data["texcoords"].append(Point2(x*u, y*u)) elif normalVec[1]>0: vi = trig.addVertex(x, z) data["texcoords"].append(Point2(x*u, z*u)) else: vi = trig.addVertex(y, z) data["texcoords"].append(Point2(y*u, z*u)) data["vertices"].append(Point3(x, y, z)) data["normals"].append(normalVec) data["colors"].append(color) trig.addPolygonVertex(vi) trig.triangulate() for i in xrange(trig.getNumTriangles()): A, B, C = trig.getTriangleV0(i), trig.getTriangleV1(i), trig.getTriangleV2(i) data["prims"].append((A, B, C)) #if normalVec[2]>0: # data["prims"].append((A, B, C)) #else: # data["prims"].append((A, C, B)) return data
def create_GeomNode_Simple_Polygon_without_Hole(symbol_geometries): color_vec4 = Vec4(1., 1., 1., 1.) outerpolygon_contour_points = 0.1 * symbol_geometries[0][0] # inner_hole_contour_points = 0.1 * symbol_geometries[0][1] # outerpolygon_contour_points = ( # np.array([[0, 1], [-1, 0], [0, -1], [1, 0]], dtype=np.float64)) # inner_hole_contour_points = ( # 0.5 * np.array([[0, 1], [-1, 0], [0, -1], [1, 0]], dtype=np.float64)) from panda3d.core import Triangulator, LPoint2d tr = Triangulator() for vertex in outerpolygon_contour_points: vi = tr.addVertex(vertex[0], vertex[1]) tr.addPolygonVertex(vi) # tr.beginHole() # for vertex in inner_hole_contour_points: # vi = tr.addVertex(vertex[0], vertex[1]) # tr.addHoleVertex(vi) tr.triangulate() vertices = tr.getVertices() indices = [] num_triangles = tr.getNumTriangles() for i in range(num_triangles): indices.append([tr.getTriangleV0(i), tr.getTriangleV1(i), tr.getTriangleV2(i)]) # Own Geometry # format = GeomVertexFormat.getV3c4t2() format = GeomVertexFormat.getV3c4() vdata = GeomVertexData("colored_polygon", format, Geom.UHStatic) vdata.setNumRows(4) # let's also add color to each vertex colorWriter = GeomVertexWriter(vdata, "color") vertexPosWriter = GeomVertexWriter(vdata, "vertex") for v in vertices: vertexPosWriter.addData3f(v[0], 0, v[1]) colorWriter.addData4f(color_vec4) # make primitives and assign vertices to them (primitives and primitive # groups can be made independently from vdata, and are later assigned # to vdata) tris = GeomTriangles(Geom.UHStatic) for index_triple in indices: tris.addVertices(index_triple[0], index_triple[1], index_triple[2]) tris.closePrimitive() # make a Geom object to hold the primitives polygonGeom = Geom(vdata) # vdata contains the vertex position/color/... buffers polygonGeom.addPrimitive(tris) # tris contains the index buffer # now put quadGeom in a GeomNode. You can now position your geometry # in the scene graph. polygonGeomNode = GeomNode("colored_polygon_node") polygonGeomNode.addGeom(polygonGeom) return polygonGeomNode
def create_GeomNode_Simple_Polygon_with_Hole(symbol_geometries): color_vec4 = Vec4(1., 1., 1., 1.) outerpolygon_contour_points = 0.1 * symbol_geometries[0][0] inner_hole_contour_points = 0.1 * symbol_geometries[0][1] from itertools import groupby inner_hole_contour_points = [k for k,g in groupby(inner_hole_contour_points.tolist())] # inner_hole_contour_points.append(inner_hole_contour_points[0]) inner_hole_contour_points = np.array(inner_hole_contour_points)[:-1] outerpolygon_contour_points = [k for k,g in groupby(outerpolygon_contour_points.tolist())] # outerpolygon_contour_points.append(outerpolygon_contour_points[0]) outerpolygon_contour_points = np.array(outerpolygon_contour_points)[:-1] # remove consecutive doubles in contour # outerpolygon_contour_points = inner_hole_contour_points # outerpolygon_contour_points = ( # np.array([[0, 1], [-1, 0], [0, -1], [1, 0]], dtype=np.float64)) # inner_hole_contour_points = ( # 0.5 * np.array([[0, 1], [-1, 0], [0, -1], [1, 0]], dtype=np.float64)) tr = Triangulator() # very simple triangular hole # v1 = np.array([ # np.amax(outerpolygon_contour_points[:, 0]), # np.amax(outerpolygon_contour_points[:, 1]), # ]) + np.array([-0.2, -0.15]) # v2 = v1 + np.array([-0.05, -0.05]) # v3 = v1 + np.array([0.0, -0.05]) # inner_hole_contour_points = [v1, v2, v3] for vertex in outerpolygon_contour_points: vi = tr.addVertex(vertex[0], vertex[1]) tr.addPolygonVertex(vi) tr.beginHole() for vertex in inner_hole_contour_points: vi = tr.addVertex(vertex[0], vertex[1]) tr.addHoleVertex(vi) tr.triangulate() vertices = tr.getVertices() indices = [] num_triangles = tr.getNumTriangles() for i in range(num_triangles): indices.append([tr.getTriangleV0(i), tr.getTriangleV1(i), tr.getTriangleV2(i)]) # Own Geometry # format = GeomVertexFormat.getV3c4t2() format = GeomVertexFormat.getV3c4() vdata = GeomVertexData("colored_polygon", format, Geom.UHStatic) vdata.setNumRows(4) # let's also add color to each vertex colorWriter = GeomVertexWriter(vdata, "color") vertexPosWriter = GeomVertexWriter(vdata, "vertex") for v in vertices: vertexPosWriter.addData3f(v[0], 0, v[1]) colorWriter.addData4f(color_vec4) # make primitives and assign vertices to them (primitives and primitive # groups can be made independently from vdata, and are later assigned # to vdata) tris = GeomTriangles(Geom.UHStatic) for index_triple in indices: tris.addVertices(index_triple[0], index_triple[1], index_triple[2]) tris.closePrimitive() # make a Geom object to hold the primitives geom = Geom(vdata) # vdata contains the vertex position/color/... buffers geom.addPrimitive(tris) # tris contains the index buffer # now put geom in a GeomNode geom_node = GeomNode("colored_polygon_node") geom_node.addGeom(geom) return geom_node
def create_plane_mesh(vertices, vertices_floor, textures, texture_floor, texture_ceiling, delta_height, ignore_ceiling=False): # create mesh for 3D floorplan visualization triangles = [] triangle_uvs = [] # the number of vertical walls num_walls = len(vertices) # 1. vertical wall (always rectangle) num_vertices = 0 for i in range(len(vertices)): # hardcode triangles for each vertical wall triangle = np.array([[0, 2, 1], [2, 0, 3]]) triangles.append(triangle + num_vertices) num_vertices += 4 triangle_uv = np.array( [ [i / (num_walls + 2), 0], [i / (num_walls + 2), 1], [(i+1) / (num_walls + 2), 1], [(i+1) / (num_walls + 2), 0] ], dtype=np.float32 ) triangle_uvs.append(triangle_uv) # 2. floor and ceiling # Since the floor and ceiling may not be a rectangle, triangulate the polygon first. tri = Triangulator() for i in range(len(vertices_floor)): tri.add_vertex(vertices_floor[i, 0], vertices_floor[i, 1]) for i in range(len(vertices_floor)): tri.add_polygon_vertex(i) tri.triangulate() # polygon triangulation triangle = [] for i in range(tri.getNumTriangles()): triangle.append([tri.get_triangle_v0(i), tri.get_triangle_v1(i), tri.get_triangle_v2(i)]) triangle = np.array(triangle) # add triangles for floor and ceiling triangles.append(triangle + num_vertices) num_vertices += len(np.unique(triangle)) if not ignore_ceiling: triangles.append(triangle + num_vertices) # texture for floor and ceiling vertices_floor_min = np.min(vertices_floor[:, :2], axis=0) vertices_floor_max = np.max(vertices_floor[:, :2], axis=0) # normalize to [0, 1] triangle_uv = (vertices_floor[:, :2] - vertices_floor_min) / (vertices_floor_max - vertices_floor_min) triangle_uv[:, 0] = (triangle_uv[:, 0] + num_walls) / (num_walls + 2) triangle_uvs.append(triangle_uv) # normalize to [0, 1] triangle_uv = (vertices_floor[:, :2] - vertices_floor_min) / (vertices_floor_max - vertices_floor_min) triangle_uv[:, 0] = (triangle_uv[:, 0] + num_walls + 1) / (num_walls + 2) triangle_uvs.append(triangle_uv) # 3. Merge wall, floor, and ceiling vertices.append(vertices_floor) vertices.append(vertices_floor + delta_height) vertices = np.concatenate(vertices, axis=0) triangles = np.concatenate(triangles, axis=0) textures.append(texture_floor) textures.append(texture_ceiling) textures = np.concatenate(textures, axis=1) triangle_uvs = np.concatenate(triangle_uvs, axis=0) mesh = open3d.geometry.TriangleMesh( vertices=open3d.utility.Vector3dVector(vertices), triangles=open3d.utility.Vector3iVector(triangles) ) mesh.compute_vertex_normals() mesh.texture = open3d.geometry.Image(textures) mesh.triangle_uvs = np.array(triangle_uvs[triangles.reshape(-1), :], dtype=np.float64) return mesh
def makeTriMesh( verts, holeVerts=[[]]): pointmap = (lambda x, y: (x, y, 0)) if not hasattr(holeVerts[0], '__iter__'): holeVerts = [holeVerts] frmt = GeomVertexFormat.getV3n3cp() vdata = GeomVertexData('triangle', frmt, Geom.UHDynamic) vertex = GeomVertexWriter(vdata, 'vertex') normal = GeomVertexWriter(vdata, 'normal') color = GeomVertexWriter(vdata, 'color') bl = Vec4(50, 50, 50, 255) gr = Vec4(256/2 - 1, 256/2 - 1, 256/2 - 1, 255) trilator = Triangulator() zUp = Vec3(0, 0, 1) for i in verts: #print "verts", verts trilator.addPolygonVertex(trilator.addVertex(i.x, i.y)) vertex.addData3f(pointmap(i.x, i.y)) normal.addData3f(zUp) color.addData4f(bl) #if len(holeVerts) != 1 and holeVerts[0] != []: for w in holeVerts: trilator.beginHole() print "new hole" for j in w: # print(j) # ###################### PRINT ####################### trilator.addHoleVertex(trilator.addVertex(j.x, j.y)) vertex.addData3f(pointmap(j.x, j.y)) normal.addData3f(zUp) color.addData4f(gr) # try: trilator.triangulate() triVerts = trilator.getVertices() p = trilator.getTriangleV0(0) print "trilator return", p # except AssertionError: # pass # TODO:re-triangulate here and change AdjacencyList to expect a list of triangles rather than call Triangulator funcs trilator = makeDelaunayTriangulation(trilator) prim = GeomTriangles(Geom.UHStatic) if isinstance(trilator, Triangulator): # HACK just to switch back and forth from the non-Delaunay to the Delaunay for school for n in xrange(trilator.getNumTriangles()): prim.addVertices(trilator.getTriangleV0(n), trilator.getTriangleV1(n), trilator.getTriangleV2(n)) else: # it's an adjacency list for n in xrange(len(trilator.adjLst)): for v in range(0, len(triVerts)): print "\ncurr", triVerts[v], "\nv0", Point2D(trilator.adjLst[n].tri[0].x, trilator.adjLst[n].tri[0].y),\ "\nv1", Point2D(trilator.adjLst[n].tri[1].x, trilator.adjLst[n].tri[1].y),\ "\nv2", Point2D(trilator.adjLst[n].tri[2].x, trilator.adjLst[n].tri[2].y) # trilator.adjLst[n].tri[0].getXy(),\ # "\nv1", trilator.adjLst[n].tri[1].getXy(),\ # "\nv2", trilator.adjLst[n].tri[2].getXy() # v0 = v1 = v2 = -1 if Point2D(trilator.adjLst[n].tri[0].x, trilator.adjLst[n].tri[0].y) == triVerts[v]: v0 = v # we need indices into the vertex pool print "found v0", v0 if Point2D(trilator.adjLst[n].tri[1].x, trilator.adjLst[n].tri[1].y) == triVerts[v]: v1 = v print "found v1", v1 if Point2D(trilator.adjLst[n].tri[2].x, trilator.adjLst[n].tri[2].y) == triVerts[v]: v2 = v print "found v2", v2 # if v0 == -1 or v1 == -1 or v2 == -1: # print "pass", v0, v1, v2 # pass # else: # print "add" # i = triVerts[v0] # vertex.addData3f(pointmap(i.x, i.y)) # normal.addData3f(zUp) # color.addData4f(bl) # i = triVerts[v1] # vertex.addData3f(pointmap(i.x, i.y)) # normal.addData3f(zUp) # color.addData4f(bl) # i = triVerts[v2] # vertex.addData3f(pointmap(i.x, i.y)) # normal.addData3f(zUp) # color.addData4f(bl) print "v 1 2 3", v0, v1, v2 prim.addVertices(v0, v1, v2) prim.closePrimitive() geom = Geom(vdata) geom.addPrimitive(prim) node = GeomNode('gnode') node.addGeom(geom) # print trilator.isLeftWinding() # HACK just for school if hasattr(trilator, "adLst"): return tuple((node, trilator.adjLst)) else: return tuple((node, trilator))
def __init__(self, items, parent=None, buttonThrower=None, onDestroy=None, font=None, baselineOffset=.0, scale=.05, itemHeight=1., leftPad=.0, separatorHeight=.5, underscoreThickness=1, BGColor=(0, 0, 0, .7), BGBorderColor=(1, .85, .4, 1), separatorColor=(1, 1, 1, 1), frameColorHover=(1, .85, .4, 1), frameColorPress=(0, 1, 0, 1), textColorReady=(1, 1, 1, 1), textColorHover=(0, 0, 0, 1), textColorPress=(0, 0, 0, 1), textColorDisabled=(.5, .5, .5, 1), minZ=None, useMouseZ=True): ''' items : a collection of menu items Item format : ( 'Item text', 'path/to/image', command ) OR ( 'Item text', 'path/to/image', command, arg1,arg2,.... ) If you don't want to use an image, pass 0. To create disabled item, pass 0 for the command : ( 'Item text', 'path/to/image', 0 ) so, you can easily switch between enabled or disabled : ( 'Item text', 'path/to/image', command if commandEnabled else 0 ) OR ( 'Item text', 'path/to/image', (0,command)[commandEnabled] ) To create submenu, pass a sequence of submenu items for the command. To create disabled submenu, pass an empty sequence for the command. To enable hotkey, insert an underscore before the character, e.g. hotkey of 'Item te_xt' is 'x' key. To add shortcut key text at the right side of the item, append it at the end of the item text, separated by "more than" sign, e.g. 'Item text>Ctrl-T'. To insert separator line, pass 0 for the whole item. parent : where to attach the menu, defaults to aspect2d buttonThrower : button thrower whose thrown events are blocked temporarily when the menu is displayed. If not given, the default button thrower is used onDestroy : user function which will be called after the menu is fully destroyed font : text font baselineOffset : text's baseline Z offset scale : text scale itemHeight : spacing between items, defaults to 1 leftPad : blank space width before text separatorHeight : separator line height, relative to itemHeight underscoreThickness : underscore line thickness BGColor, BGBorderColor, separatorColor, frameColorHover, frameColorPress, textColorReady, textColorHover, textColorPress, textColorDisabled are some of the menu components' color minZ : minimum Z position to restrain menu's bottom from going offscreen (-1..1). If it's None, it will be set a little above the screen's bottom. ''' self.parent = parent if parent else aspect2d self.onDestroy = onDestroy self.BT = buttonThrower if buttonThrower else base.buttonThrowers[ 0].node() self.menu = NodePath('menu-%s' % id(self)) self.parentMenu = None self.submenu = None self.BTprefix = self.menu.getName() + '>' self.submenuCreationTaskName = 'createSubMenu-' + self.BTprefix self.submenuRemovalTaskName = 'removeSubMenu-' + self.BTprefix self.font = font if font else TextNode.getDefaultFont() self.baselineOffset = baselineOffset if isinstance(scale, (float, int)): scale = (scale, 1.0, scale) self.scale = scale self.itemHeight = itemHeight self.leftPad = leftPad self.separatorHeight = separatorHeight self.underscoreThickness = underscoreThickness self.BGColor = BGColor self.BGBorderColor = BGBorderColor self.separatorColor = separatorColor self.frameColorHover = frameColorHover self.frameColorPress = frameColorPress self.textColorReady = textColorReady self.textColorHover = textColorHover self.textColorPress = textColorPress self.textColorDisabled = textColorDisabled self.minZ = minZ self.mpos = Point2(base.mouseWatcherNode.getMouse()) self.itemCommand = [] self.hotkeys = {} self.numItems = 0 self.sel = -1 self.selByKey = False bgPad = self.bgPad = .0125 texMargin = self.font.getTextureMargin() * self.scale[0] * .25 b = DirectButton(parent=NodePath(''), text='^|g_', text_font=self.font, scale=self.scale) fr = b.node().getFrame() b.getParent().removeNode() baselineToCenter = (fr[2] + fr[3]) * self.scale[0] LH = (fr[3] - fr[2]) * self.itemHeight * self.scale[2] imageHalfHeight = .5 * (fr[3] - fr[2]) * self.itemHeight * .85 arrowHalfHeight = .5 * (fr[3] - fr[2]) * self.itemHeight * .5 baselineToTop = (fr[3] * self.itemHeight * self.scale[2] / LH) / (1. + self.baselineOffset) baselineToBot = LH / self.scale[2] - baselineToTop itemZcenter = (baselineToTop - baselineToBot) * .5 separatorHalfHeight = .5 * separatorHeight * LH LSseparator = LineSegs() LSseparator.setColor(.5, .5, .5, .2) arrowVtx = [ (0, itemZcenter), (-2 * arrowHalfHeight, itemZcenter + arrowHalfHeight), (-arrowHalfHeight, itemZcenter), (-2 * arrowHalfHeight, itemZcenter - arrowHalfHeight), ] tri = Triangulator() vdata = GeomVertexData('trig', GeomVertexFormat.getV3(), Geom.UHStatic) vwriter = GeomVertexWriter(vdata, 'vertex') for x, z in arrowVtx: vi = tri.addVertex(x, z) vwriter.addData3f(x, 0, z) tri.addPolygonVertex(vi) tri.triangulate() prim = GeomTriangles(Geom.UHStatic) for i in range(tri.getNumTriangles()): prim.addVertices(tri.getTriangleV0(i), tri.getTriangleV1(i), tri.getTriangleV2(i)) prim.closePrimitive() geom = Geom(vdata) geom.addPrimitive(prim) geomNode = GeomNode('arrow') geomNode.addGeom(geom) realArrow = NodePath(geomNode) z = -baselineToTop * self.scale[2] - bgPad maxWidth = .1 / self.scale[0] shortcutTextMaxWidth = 0 anyImage = False anyArrow = False anyShortcut = False arrows = [] shortcutTexts = [] loadPrcFileData('', 'text-flatten 0') for item in items: if item: t, imgPath, f = item[:3] haveSubmenu = type(f) in SEQUENCE_TYPES anyArrow |= haveSubmenu anyImage |= isinstance(imgPath, bool) or bool(imgPath) disabled = not len(f) if haveSubmenu else not callable(f) args = item[3:] underlinePos = t.find('_') t = t.replace('_', '') shortcutSepPos = t.find('>') if shortcutSepPos > -1: if haveSubmenu: print( "\nA SHORTCUT KEY POINTING TO A SUBMENU IS NON-SENSE, DON'T YOU AGREE ?" ) else: shortcutText = NodePath( OnscreenText( parent=self.menu, text=t[shortcutSepPos + 1:], font=self.font, scale=1, fg=(1, 1, 1, 1), align=TextNode.ARight, )) shortcutTextMaxWidth = max( shortcutTextMaxWidth, abs(shortcutText.getTightBounds()[0][0])) anyShortcut = True t = t[:shortcutSepPos] else: shortcutText = '' EoLcount = t.count('\n') arrowZpos = -self.font.getLineHeight() * EoLcount * .5 if disabled: b = NodePath( OnscreenText( parent=self.menu, text=t, font=self.font, scale=1, fg=textColorDisabled, align=TextNode.ALeft, )) # don't pass the scale and position to OnscreenText constructor, # to maintain correctness between the OnscreenText and DirectButton items # due to the new text generation implementation b.setScale(self.scale) b.setZ(z) maxWidth = max(maxWidth, b.getTightBounds()[1][0] / self.scale[0]) if shortcutText: shortcutText.reparentTo(b) shortcutText.setColor(Vec4(*textColorDisabled), 1) shortcutText.setZ(arrowZpos) shortcutTexts.append(shortcutText) else: b = DirectButton( parent=self.menu, text=t, text_font=self.font, scale=self.scale, pos=(0, 0, z), text_fg=textColorReady, # text color when mouse over text2_fg=textColorHover, # text color when pressed text1_fg=textColorHover if haveSubmenu else textColorPress, # framecolor when pressed frameColor=frameColorHover if haveSubmenu else frameColorPress, commandButtons=[DGG.LMB, DGG.RMB], command=(lambda: 0) if haveSubmenu else self.__runCommand, extraArgs=[] if haveSubmenu else [f, args], text_align=TextNode.ALeft, relief=DGG.FLAT, rolloverSound=0, clickSound=0, pressEffect=0) b.stateNodePath[2].setColor( *frameColorHover) # framecolor when mouse over b.stateNodePath[0].setColor(0, 0, 0, 0) # framecolor when ready bframe = Vec4(b.node().getFrame()) if EoLcount: bframe.setZ(EoLcount * 10) b['frameSize'] = bframe maxWidth = max(maxWidth, bframe[1]) if shortcutText: for snpi, col in ((0, textColorReady), (1, textColorPress), (2, textColorHover)): sct = shortcutText.copyTo(b.stateNodePath[snpi], sort=10) sct.setColor(Vec4(*col), 1) sct.setZ(arrowZpos) shortcutTexts.append(sct) shortcutText.removeNode() if isinstance(imgPath, bool): if imgPath: if disabled: fg = textColorDisabled else: fg = textColorReady tick = NodePath( OnscreenText( parent=b, text=u"\u2714", font=self.font, scale=1, fg=fg, align=TextNode.ALeft, )) tick.setX(-2 * imageHalfHeight - leftPad) elif imgPath: img = loader.loadTexture(imgPath, okMissing=True) if img is not None: if disabled: if imgPath in PopupMenu.grayImages: img = PopupMenu.grayImages[imgPath] else: pnm = PNMImage() img.store(pnm) pnm.makeGrayscale(.2, .2, .2) img = Texture() img.load(pnm) PopupMenu.grayImages[imgPath] = img img.setMinfilter(Texture.FTLinearMipmapLinear) img.setWrapU(Texture.WMClamp) img.setWrapV(Texture.WMClamp) CM = CardMaker('') CM.setFrame(-2 * imageHalfHeight - leftPad, -leftPad, itemZcenter - imageHalfHeight, itemZcenter + imageHalfHeight) imgCard = b.attachNewNode(CM.generate()) imgCard.setTexture(img) if underlinePos > -1: oneLineText = t[:underlinePos + 1] oneLineText = oneLineText[oneLineText.rfind('\n') + 1:] tn = TextNode('') tn.setFont(self.font) tn.setText(oneLineText) tnp = NodePath(tn.getInternalGeom()) underlineXend = tnp.getTightBounds()[1][0] tnp.removeNode() tn.setText(t[underlinePos]) tnp = NodePath(tn.getInternalGeom()) b3 = tnp.getTightBounds() underlineXstart = underlineXend - (b3[1] - b3[0])[0] tnp.removeNode() underlineZpos = -.7 * baselineToBot - self.font.getLineHeight( ) * t[:underlinePos].count('\n') LSunder = LineSegs() LSunder.setThickness(underscoreThickness) LSunder.moveTo(underlineXstart + texMargin, 0, underlineZpos) LSunder.drawTo(underlineXend - texMargin, 0, underlineZpos) if disabled: underline = b.attachNewNode(LSunder.create()) underline.setColor(Vec4(*textColorDisabled), 1) else: underline = b.stateNodePath[0].attachNewNode( LSunder.create()) underline.setColor(Vec4(*textColorReady), 1) underline.copyTo(b.stateNodePath[1], 10).setColor( Vec4(*textColorHover if haveSubmenu else textColorPress), 1) underline.copyTo(b.stateNodePath[2], 10).setColor(Vec4(*textColorHover), 1) hotkey = t[underlinePos].lower() if hotkey in self.hotkeys: self.hotkeys[hotkey].append(self.numItems) else: self.hotkeys[hotkey] = [self.numItems] self.accept(self.BTprefix + hotkey, self.__processHotkey, [hotkey]) self.accept(self.BTprefix + 'alt-' + hotkey, self.__processHotkey, [hotkey]) if haveSubmenu: if disabled: arrow = realArrow.instanceUnderNode(b, '') arrow.setColor(Vec4(*textColorDisabled), 1) arrow.setZ(arrowZpos) else: arrow = realArrow.instanceUnderNode( b.stateNodePath[0], 'r') arrow.setColor(Vec4(*textColorReady), 1) arrow.setZ(arrowZpos) arrPress = realArrow.instanceUnderNode( b.stateNodePath[1], 'p') arrPress.setColor(Vec4(*textColorHover), 1) arrPress.setZ(arrowZpos) arrHover = realArrow.instanceUnderNode( b.stateNodePath[2], 'h') arrHover.setColor(Vec4(*textColorHover), 1) arrHover.setZ(arrowZpos) # weird, if sort order is 0, it's obscured by the frame for a in (arrPress, arrHover): a.reparentTo(a.getParent(), sort=10) if not disabled: extraArgs = [self.numItems, f if haveSubmenu else 0] self.accept(DGG.ENTER + b.guiId, self.__hoverOnItem, extraArgs) self.accept(DGG.EXIT + b.guiId, self.__offItem) #~ self.itemCommand.append((None,0) if haveSubmenu else (f,args)) self.itemCommand.append((f, args)) if self.numItems == 0: self.firstButtonIdx = int(b.guiId[2:]) self.numItems += 1 z -= LH + self.font.getLineHeight() * self.scale[2] * EoLcount else: # SEPARATOR LINE z += LH - separatorHalfHeight - baselineToBot * self.scale[2] LSseparator.moveTo(0, 0, z) LSseparator.drawTo(self.scale[0] * .5, 0, z) LSseparator.drawTo(self.scale[0], 0, z) z -= separatorHalfHeight + baselineToTop * self.scale[2] maxWidth += 7 * arrowHalfHeight * ( anyArrow or anyShortcut) + .2 + shortcutTextMaxWidth arrowXpos = maxWidth - arrowHalfHeight realArrow.setX(arrowXpos) if anyImage: leftPad += 2 * imageHalfHeight + leftPad for sct in shortcutTexts: sct.setX(maxWidth - 2 * (arrowHalfHeight * anyArrow + .2)) for c in asList(self.menu.findAllMatches('**/DirectButton*')): numLines = c.node().getFrame()[2] c.node().setFrame( Vec4( -leftPad, maxWidth, -baselineToBot - (numLines * .1 * self.itemHeight if numLines >= 10 else 0), baselineToTop)) loadPrcFileData('', 'text-flatten 1') try: minZ = self.menu.getChild(0).getRelativePoint( b, Point3(0, 0, b.node().getFrame()[2]))[2] except: minZ = self.menu.getChild(0).getRelativePoint( self.menu, Point3( 0, 0, b.getTightBounds()[0][2]))[2] - baselineToBot * .5 try: top = self.menu.getChild(0).node().getFrame()[3] except: top = self.menu.getChild(0).getZ() + baselineToTop l, r, b, t = -leftPad - bgPad / self.scale[ 0], maxWidth + bgPad / self.scale[0], minZ - bgPad / self.scale[ 2], top + bgPad / self.scale[2] menuBG = DirectFrame(parent=self.menu.getChild(0), frameSize=(l, r, b, t), frameColor=BGColor, state=DGG.NORMAL, suppressMouse=1) menuBorder = self.menu.getChild(0).attachNewNode('border') borderVtx = ( (l, 0, b), (l, 0, .5 * (b + t)), (l, 0, t), (.5 * (l + r), 0, t), (r, 0, t), (r, 0, .5 * (b + t)), (r, 0, b), (.5 * (l + r), 0, b), (l, 0, b), ) LSborderBG = LineSegs() LSborderBG.setThickness(4) LSborderBG.setColor(0, 0, 0, .7) LSborderBG.moveTo(*(borderVtx[0])) for v in borderVtx[1:]: LSborderBG.drawTo(*v) # fills the gap at corners for v in range(0, 7, 2): LSborderBG.moveTo(*(borderVtx[v])) menuBorder.attachNewNode(LSborderBG.create()) LSborder = LineSegs() LSborder.setThickness(2) LSborder.setColor(*BGBorderColor) LSborder.moveTo(*(borderVtx[0])) for v in borderVtx[1:]: LSborder.drawTo(*v) menuBorder.attachNewNode(LSborder.create()) for v in range(1, 8, 2): LSborderBG.setVertexColor(v, Vec4(0, 0, 0, .1)) LSborder.setVertexColor(v, Vec4(.3, .3, .3, .5)) menuBorderB3 = menuBorder.getTightBounds() menuBorderDims = menuBorderB3[1] - menuBorderB3[0] menuBG.wrtReparentTo(self.menu, sort=-1) self.menu.reparentTo(self.parent) x = -menuBorderB3[0][0] * self.scale[0] for c in asList(self.menu.getChildren()): c.setX(x) self.maxWidth = maxWidth = menuBorderDims[0] self.height = menuBorderDims[2] maxWidthR2D = maxWidth * self.menu.getChild(0).getSx(render2d) separatorLines = self.menu.attachNewNode(LSseparator.create(), 10) separatorLines.setSx(maxWidth) for v in range(1, LSseparator.getNumVertices(), 3): LSseparator.setVertexColor(v, Vec4(*separatorColor)) x = clamp(-.98, .98 - maxWidthR2D, self.mpos[0] - maxWidthR2D * .5) minZ = (-.98 if self.minZ is None else self.minZ) z = clamp( minZ + menuBorderDims[2] * self.scale[2] * self.parent.getSz(render2d), .98, self.mpos[1] if useMouseZ else -1000) self.menu.setPos(render2d, x, 0, z) self.menu.setTransparency(1) self.origBTprefix = self.BT.getPrefix() self.BT.setPrefix(self.BTprefix) self.accept(self.BTprefix + 'escape', self.destroy) for e in ('mouse1', 'mouse3'): self.accept(self.BTprefix + e, self.destroy, [True]) self.accept(self.BTprefix + 'arrow_down', self.__nextItem) self.accept(self.BTprefix + 'arrow_down-repeat', self.__nextItem) self.accept(self.BTprefix + 'arrow_up', self.__prevItem) self.accept(self.BTprefix + 'arrow_up-repeat', self.__prevItem) self.accept(self.BTprefix + 'enter', self.__runSelItemCommand) self.accept(self.BTprefix + 'space', self.__runSelItemCommand)
def makeTriMesh( verts, holeVerts=[[]]): pointmap = (lambda x, y: (x, y, 0)) if not holeVerts: holeVerts = [[]] if not hasattr(holeVerts[0], '__iter__'): holeVerts = [holeVerts] frmt = GeomVertexFormat.getV3n3cp() vdata = GeomVertexData('triangle', frmt, Geom.UHDynamic) vertex = GeomVertexWriter(vdata, 'vertex') normal = GeomVertexWriter(vdata, 'normal') color = GeomVertexWriter(vdata, 'color') bl = Vec4(50, 50, 50, 255) gr = Vec4(256/2 - 1, 256/2 - 1, 256/2 - 1, 255) trilator = Triangulator() zUp = Vec3(0, 0, 1) for i in verts: #print "verts", verts trilator.addPolygonVertex(trilator.addVertex(i.x, i.y)) vertex.addData3f(pointmap(i.x, i.y)) normal.addData3f(zUp) color.addData4f(bl) #if len(holeVerts) != 1 and holeVerts[0] != []: for w in holeVerts: trilator.beginHole() print "new hole" for j in w: # print(j) # ###################### PRINT ####################### trilator.addHoleVertex(trilator.addVertex(j.x, j.y)) vertex.addData3f(pointmap(j.x, j.y)) normal.addData3f(zUp) color.addData4f(gr) # try: trilator.triangulate() triVerts = trilator.getVertices() p = trilator.getTriangleV0(0) print "trilator return", p # except AssertionError: # pass # TODO:re-triangulate here and change AdjacencyList to expect a list of triangles rather than call Triangulator funcs trilator = makeDelaunayTriangulation(trilator) prim = GeomTriangles(Geom.UHStatic) if isinstance(trilator, Triangulator): # HACK just to switch back and forth from the non-Delaunay to the Delaunay for school for n in xrange(trilator.getNumTriangles()): prim.addVertices(trilator.getTriangleV0(n), trilator.getTriangleV1(n), trilator.getTriangleV2(n)) else: # it's an adjacency list for n in xrange(len(trilator.adjLst)): for v in range(0, len(triVerts)): print "\ncurr", triVerts[v], "\nv0", Point2D(trilator.adjLst[n].tri[0].x, trilator.adjLst[n].tri[0].y),\ "\nv1", Point2D(trilator.adjLst[n].tri[1].x, trilator.adjLst[n].tri[1].y),\ "\nv2", Point2D(trilator.adjLst[n].tri[2].x, trilator.adjLst[n].tri[2].y) # trilator.adjLst[n].tri[0].getXy(),\ # "\nv1", trilator.adjLst[n].tri[1].getXy(),\ # "\nv2", trilator.adjLst[n].tri[2].getXy() # v0 = v1 = v2 = -1 if Point2D(trilator.adjLst[n].tri[0].x, trilator.adjLst[n].tri[0].y) == triVerts[v]: v0 = v # we need indices into the vertex pool print "found v0", v0 if Point2D(trilator.adjLst[n].tri[1].x, trilator.adjLst[n].tri[1].y) == triVerts[v]: v1 = v print "found v1", v1 if Point2D(trilator.adjLst[n].tri[2].x, trilator.adjLst[n].tri[2].y) == triVerts[v]: v2 = v print "found v2", v2 # if v0 == -1 or v1 == -1 or v2 == -1: # print "pass", v0, v1, v2 # pass # else: # print "add" # i = triVerts[v0] # vertex.addData3f(pointmap(i.x, i.y)) # normal.addData3f(zUp) # color.addData4f(bl) # i = triVerts[v1] # vertex.addData3f(pointmap(i.x, i.y)) # normal.addData3f(zUp) # color.addData4f(bl) # i = triVerts[v2] # vertex.addData3f(pointmap(i.x, i.y)) # normal.addData3f(zUp) # color.addData4f(bl) print "v 1 2 3", v0, v1, v2 prim.addVertices(v0, v1, v2) prim.closePrimitive() geom = Geom(vdata) geom.addPrimitive(prim) node = GeomNode('gnode') node.addGeom(geom) # print trilator.isLeftWinding() # HACK just for school if hasattr(trilator, "adLst"): return tuple((node, trilator.adjLst)) else: return tuple((node, trilator))