Q81:“三角形网格”之“PLY文件”

2023-11-12

 

81.1 引入

 

在“Q79”和“Q80”中用三角形网格细分曲面时,都是将每一个三角形的三个顶点的坐标都保存在内存中。这句话有两个重点:

其一,每个三角形的三个顶点的坐标都计算了一次。但是,每个顶点都是被好几个三角形公用的,所以每个顶点的坐标被重复计算了好几次。

其二,所有顶点的坐标都是临时计算的,然后保存到内存中的。如果顶点数量特别大(百万级、千万级),这样临时计算则会导致程序运行缓慢。是否可先将顶点的坐标都算好保存在文件中,程序运行时直接读这个文件就可以了呢?

 

这种保存“将图形用三角形网格细分后得到的三角形信息”的文件被称为“PLY文件”。

 

81.2 PLY文件的格式

 

PLY文件由两部分组成:文件头、文件体。

 

文件头是由以“回车(carriage-return)”为结束符的ASCII字符串组成的。

 

文件体是由“元素”组成。一般情况,会包含两类“元素”:第一类,顶点信息(含顶点的坐标值、法向量、纹理映射值等等);第二类,多边形各个顶点在第一类元素中的索引值。

 

如下方示意:


 

PLY文件的内容是非常灵活的。第一类元素“顶点vertex”,除了可以包含顶点的坐标值,还可以包含顶点的“法向量”、“纹理坐标”、“颜色值”等等;第二类元素“face”,其实是可以表示任意多边形的,而且不同顶点数的各种多边形可以同时被包含在同一个PLY文件中。

 

81.3 将经典的PLY文件从Unix/Mac系统下的格式转换到Windows系统下的格式

 

参考:

http://blog.csdn.net/libing_zeng/article/details/60878097

 

转换后的PLY文件截图如下:


这些文件的路径:http://download.csdn.net/detail/libing_zeng/9775786

 

81.4 程序读取PLY文件

 

Greg Turk发明PLY文件格式之后,就有提供通用的可拓展的读取PLY文件的程序。相关链接如下:http://www.cc.gatech.edu/projects/large_models/ply.html

 

但是,本人是直接移植了《Ray Tracing from the Ground Up》的相关代码。在移植过程中遇到两个问题:




 

《Ray Tracing from the Ground Up》中提供的调用读取PLY文件接口的函数如下:

 

// ----------------------------------------------------------------------------- read_ply_file

// Most of this function was written by Greg Turk and is released under the licence agreement
// at the start of the PLY.h and PLY.c files
// The PLY.h file is #included at the start of this file
// It still has some of his printf statements for debugging
// I've made changes to construct mesh triangles and store them in the grid
// mesh_ptr is a data member of Grid
// objects is a data member of Compound
// triangle_type is either flat or smooth
// Using the one function construct to flat and smooth triangles saves a lot of repeated code
// The ply file is the same for flat and smooth triangles


void
Grid::read_ply_file(char* file_name, const int triangle_type) {
	// Vertex definition

	typedef struct Vertex {
	  float x,y,z;      // space coordinates
	} Vertex;

	// Face definition. This is the same for all files but is placed here to keep all the definitions together

	typedef struct Face {
	  	unsigned char nverts;    // number of vertex indices in list
	  	int* verts;              // vertex index list
	} Face;

	// list of property information for a vertex
	// this varies depending on what you are reading from the file

	PlyProperty vert_props[] = {
	  {"x", PLY_FLOAT, PLY_FLOAT, offsetof(Vertex,x), 0, 0, 0, 0},
	  {"y", PLY_FLOAT, PLY_FLOAT, offsetof(Vertex,y), 0, 0, 0, 0},
	  {"z", PLY_FLOAT, PLY_FLOAT, offsetof(Vertex,z), 0, 0, 0, 0}
	};

	// list of property information for a face.
	// there is a single property, which is a list
	// this is the same for all files

	PlyProperty face_props[] = {
	  	{"vertex_indices", PLY_INT, PLY_INT, offsetof(Face,verts),
	   		1, PLY_UCHAR, PLY_UCHAR, offsetof(Face,nverts)}
	};

	// local variables

	int 			i,j;
  	PlyFile*		ply;
  	int 			nelems;		// number of element types: 2 in our case - vertices and faces
  	char**			elist;
	int 			file_type;
	float 			version;
	int 			nprops;		// number of properties each element has
	int 			num_elems;	// number of each type of element: number of vertices or number of faces
	PlyProperty**	plist;
	Vertex**		vlist;
	Face**			flist;
	char*			elem_name;
	int				num_comments;
	char**			comments;
	int 			num_obj_info;
	char**			obj_info;


  	// open a ply file for reading

	ply = ply_open_for_reading(file_name, &nelems, &elist, &file_type, &version);

  	// print what we found out about the file

  	printf ("version %f\n", version);
  	printf ("type %d\n", file_type);

  	// go through each kind of element that we learned is in the file and read them

  	for (i = 0; i < nelems; i++) {  // there are only two elements in our files: vertices and faces
	    // get the description of the first element

  	    elem_name = elist[i];
	    plist = ply_get_element_description (ply, elem_name, &num_elems, &nprops);

	    // print the name of the element, for debugging

		cout << "element name  " << elem_name << "  num elements = " << num_elems << "  num properties =  " << nprops << endl;

	    // if we're on vertex elements, read in the properties

    	if (equal_strings ("vertex", elem_name)) {
	      	// set up for getting vertex elements
	      	// the three properties are the vertex coordinates

			ply_get_property (ply, elem_name, &vert_props[0]);
	      	ply_get_property (ply, elem_name, &vert_props[1]);
		  	ply_get_property (ply, elem_name, &vert_props[2]);

		  	// reserve mesh elements

		  	mesh_ptr->num_vertices = num_elems;
		  	mesh_ptr->vertices.reserve(num_elems);

		  	// grab all the vertex elements

		  	for (j = 0; j < num_elems; j++) {
				Vertex* vertex_ptr = new Vertex;

		        // grab an element from the file

				ply_get_element (ply, (void *) vertex_ptr);
		  		mesh_ptr->vertices.push_back(Point3D(vertex_ptr->x, vertex_ptr->y, vertex_ptr->z));
		  		delete vertex_ptr;
		  	}
    	}

	    // if we're on face elements, read them in

	    if (equal_strings ("face", elem_name)) {
		    // set up for getting face elements

			ply_get_property (ply, elem_name, &face_props[0]);   // only one property - a list

		  	mesh_ptr->num_triangles = num_elems;
		  	objects.reserve(num_elems);  // triangles will be stored in Compound::objects

			// the following code stores the face numbers that are shared by each vertex

		  	mesh_ptr->vertex_faces.reserve(mesh_ptr->num_vertices);
		  	vector<int> faceList;

		  	for (j = 0; j < mesh_ptr->num_vertices; j++)
		  		mesh_ptr->vertex_faces.push_back(faceList); // store empty lists so that we can use the [] notation below

			// grab all the face elements

			int count = 0; // the number of faces read

			for (j = 0; j < num_elems; j++) {
			    // grab an element from the file

			    Face* face_ptr = new Face;

			    ply_get_element (ply, (void *) face_ptr);

			    // construct a mesh triangle of the specified type

			    if (triangle_type == flat) {
			    	FlatMeshTriangle* triangle_ptr = new FlatMeshTriangle(mesh_ptr, face_ptr->verts[0], face_ptr->verts[1], face_ptr->verts[2]);
					triangle_ptr->compute_normal(reverse_normal);
					objects.push_back(triangle_ptr);
				}

			    if (triangle_type == smooth) {
			    	SmoothMeshTriangle* triangle_ptr = new SmoothMeshTriangle(mesh_ptr, face_ptr->verts[0], face_ptr->verts[1], face_ptr->verts[2]);
					triangle_ptr->compute_normal(reverse_normal); 	// the "flat triangle" normal is used to compute the average normal at each mesh vertex
					objects.push_back(triangle_ptr); 				// it's quicker to do it once here, than have to do it on average 6 times in compute_mesh_normals

					// the following code stores a list of all faces that share a vertex
					// it's used for computing the average normal at each vertex in order(num_vertices) time

					mesh_ptr->vertex_faces[face_ptr->verts[0]].push_back(count);
					mesh_ptr->vertex_faces[face_ptr->verts[1]].push_back(count);
					mesh_ptr->vertex_faces[face_ptr->verts[2]].push_back(count);
					count++;
				}
			}

			if (triangle_type == flat)
				mesh_ptr->vertex_faces.erase(mesh_ptr->vertex_faces.begin(), mesh_ptr->vertex_faces.end());
	    }

	    // print out the properties we got, for debugging

	    for (j = 0; j < nprops; j++)
	    	printf ("property %s\n", plist[j]->name);

	}  // end of for (i = 0; i < nelems; i++)


	// grab and print out the comments in the file

	comments = ply_get_comments (ply, &num_comments);

	for (i = 0; i < num_comments; i++)
	    printf ("comment = '%s'\n", comments[i]);

	// grab and print out the object information

	obj_info = ply_get_obj_info (ply, &num_obj_info);

	for (i = 0; i < num_obj_info; i++)
	    printf ("obj_info = '%s'\n", obj_info[i]);

	// close the ply file

	ply_close (ply);
}

简单来看,分如下几个步骤:

1,打开文件;

2,读取头文件;

3,读取顶点元素;

4,读取三角形元素,依次:根据索引在顶点元素中找到对应的顶点值,创建一个新的FlatMeshTriangle/SmoothMeshTriangle对象。

5,关闭文件;

 

其中第4步的截图如下:


 

81.5 添加Grid图形阴影

 

判断某个撞击点p是否在阴影之中,只需要判断从撞击点p到光源位置的阴影光线是否撞击到其他物体。这里我们考虑的是Grid图形的阴影,所以只需要判断阴影光线是否撞击到Grid图形。所以,我们需要实现Grid类的shadow_hit()函数:


bool
Grid::shadow_hit(const Ray& ray, double& t) const {
	double ox = ray.o.x;
	double oy = ray.o.y;
	double oz = ray.o.z;
	double dx = ray.d.x;
	double dy = ray.d.y;
	double dz = ray.d.z;

	double x0 = bbox.x0;
	double y0 = bbox.y0;
	double z0 = bbox.z0;
	double x1 = bbox.x1;
	double y1 = bbox.y1;
	double z1 = bbox.z1;

	double tx_min, ty_min, tz_min;
	double tx_max, ty_max, tz_max;

	// the following code includes modifications from Shirley and Morley (2003)

	double a = 1.0 / dx;
	if (a >= 0) {
		tx_min = (x0 - ox) * a;
		tx_max = (x1 - ox) * a;
	}
	else {
		tx_min = (x1 - ox) * a;
		tx_max = (x0 - ox) * a;
	}

	double b = 1.0 / dy;
	if (b >= 0) {
		ty_min = (y0 - oy) * b;
		ty_max = (y1 - oy) * b;
	}
	else {
		ty_min = (y1 - oy) * b;
		ty_max = (y0 - oy) * b;
	}

	double c = 1.0 / dz;
	if (c >= 0) {
		tz_min = (z0 - oz) * c;
		tz_max = (z1 - oz) * c;
	}
	else {
		tz_min = (z1 - oz) * c;
		tz_max = (z0 - oz) * c;
	}

	double t0, t1;

	if (tx_min > ty_min)
		t0 = tx_min;
	else
		t0 = ty_min;

	if (tz_min > t0)
		t0 = tz_min;

	if (tx_max < ty_max)
		t1 = tx_max;
	else
		t1 = ty_max;

	if (tz_max < t1)
		t1 = tz_max;

	if (t0 > t1)
		return(false);


	// initial cell coordinates

	int ix, iy, iz;

	if (bbox.inside(ray.o)) {  			// does the ray start inside the grid?
		ix = clamp((ox - x0) * nx / (x1 - x0), 0, nx - 1);
		iy = clamp((oy - y0) * ny / (y1 - y0), 0, ny - 1);
		iz = clamp((oz - z0) * nz / (z1 - z0), 0, nz - 1);
	}
	else {
		Point3D p = ray.o + t0 * ray.d;  // initial hit point with grid's bounding box
		ix = clamp((p.x - x0) * nx / (x1 - x0), 0, nx - 1);
		iy = clamp((p.y - y0) * ny / (y1 - y0), 0, ny - 1);
		iz = clamp((p.z - z0) * nz / (z1 - z0), 0, nz - 1);
	}

	// ray parameter increments per cell in the x, y, and z directions

	double dtx = (tx_max - tx_min) / nx;
	double dty = (ty_max - ty_min) / ny;
	double dtz = (tz_max - tz_min) / nz;

	double 	tx_next, ty_next, tz_next;
	int 	ix_step, iy_step, iz_step;
	int 	ix_stop, iy_stop, iz_stop;

	if (dx > 0) {
		tx_next = tx_min + (ix + 1) * dtx;
		ix_step = +1;
		ix_stop = nx;
	}
	else {
		tx_next = tx_min + (nx - ix) * dtx;
		ix_step = -1;
		ix_stop = -1;
	}

	if (dx == 0.0) {
		tx_next = kHugeValue;
		ix_step = -1;
		ix_stop = -1;
	}


	if (dy > 0) {
		ty_next = ty_min + (iy + 1) * dty;
		iy_step = +1;
		iy_stop = ny;
	}
	else {
		ty_next = ty_min + (ny - iy) * dty;
		iy_step = -1;
		iy_stop = -1;
	}

	if (dy == 0.0) {
		ty_next = kHugeValue;
		iy_step = -1;
		iy_stop = -1;
	}

	if (dz > 0) {
		tz_next = tz_min + (iz + 1) * dtz;
		iz_step = +1;
		iz_stop = nz;
	}
	else {
		tz_next = tz_min + (nz - iz) * dtz;
		iz_step = -1;
		iz_stop = -1;
	}

	if (dz == 0.0) {
		tz_next = kHugeValue;
		iz_step = -1;
		iz_stop = -1;
	}


	// traverse the grid

	while (true) {
		GeometricObject* object_ptr = cells[ix + nx * iy + nx * ny * iz];

		if (tx_next < ty_next && tx_next < tz_next) {
			if (object_ptr && object_ptr->shadow_hit(ray, t) && t < tx_next) {
				return (true);
			}

			tx_next += dtx;
			ix += ix_step;

			if (ix == ix_stop)
				return (false);
		}
		else {
			if (ty_next < tz_next) {
				if (object_ptr && object_ptr->shadow_hit(ray, t) && t < ty_next) {
					return (true);
				}

				ty_next += dty;
				iy += iy_step;

				if (iy == iy_stop)
					return (false);
		 	}
		 	else {
				if (object_ptr && object_ptr->shadow_hit(ray, t) && t < tz_next) {
					return (true);
				}

				tz_next += dtz;
				iz += iz_step;

				if (iz == iz_stop)
					return (false);
		 	}
		}
	}
}	// end of hit

由于Grid的每个cell对应的物体的指针可能有两种情况:

1,单个物体的指针(这里即是单个FlatMeshTriangle对象的指针)。

2,多个物体的指针,即Compound对象的指针。

 

所以,我们需要实现FlatMeshTriangle类和Compound类的shadow_hit()。当然,由于我们这里的图形是由FlatMeshTriangle组成,Compound类的shadow_hit()最终也会调用到FlatMeshTriangle类的shadow_hit()。

 

这就有个问题了:是不是可以只实现“FlatMeshTriangle类的shadow_hit()”而不用实现“Compound类的shadow_hit()”呢?

当然不行。如果这样,也就是只考虑了之前两种情况的“单个物体的指针”的情况,而忽略了“多个物体的指针”的情况。如果这样,最后看到的图形基本没有阴影,因为cell中对应的一般是多个物体的指针。


bool
Compound::shadow_hit(const Ray& ray, double& tmin) const {
	double		t;
	Normal		normal;
	Point3D		local_hit_point;
	bool		hit 		= false;
				tmin 		= kHugeValue;
	int 		num_objects	= objects.size();

	for (int j = 0; j < num_objects; j++)
		if (objects[j]->shadow_hit(ray, t) && (t < tmin)) {
			hit				= true;
			tmin 			= t;
		}

	return (hit);
}


bool
FlatMeshTriangle::shadow_hit(const Ray& ray, double& tmin) const {
	Point3D v0(mesh_ptr->vertices[index0]);
	Point3D v1(mesh_ptr->vertices[index1]);
	Point3D v2(mesh_ptr->vertices[index2]);

	double a = v0.x - v1.x, b = v0.x - v2.x, c = ray.d.x, d = v0.x - ray.o.x;
	double e = v0.y - v1.y, f = v0.y - v2.y, g = ray.d.y, h = v0.y - ray.o.y;
	double i = v0.z - v1.z, j = v0.z - v2.z, k = ray.d.z, l = v0.z - ray.o.z;

	double m = f * k - g * j, n = h * k - g * l, p = f * l - h * j;
	double q = g * i - e * k, s = e * j - f * i;

	double inv_denom  = 1.0 / (a * m + b * q + c * s);

	double e1 = d * m - b * n - c * p;
	double beta = e1 * inv_denom;

	if (beta < 0.0)
	 	return (false);

	double r = e * l - h * i;
	double e2 = a * n + d * q + c * r;
	double gamma = e2 * inv_denom;

	if (gamma < 0.0)
	 	return (false);

	if (beta + gamma > 1.0)
		return (false);

	double e3 = a * p - b * r + d * s;
	double t = e3 * inv_denom;

	if (t < kEpsilon)
		return (false);

	tmin 				= t;

	return (true);
}


81.6 测试图形

 

World;;build()函数如下:

 

void
World::build(void) {
	int num_samples = 100;

	vp.set_hres(400);
	vp.set_vres(400);
	vp.set_samples(1);

	tracer_ptr = new RayCast(this);

	background_color = black;

	Pinhole* pinhole_ptr = new Pinhole;
#if 0 // 300*300: happy, Bunny69K, dragon
	pinhole_ptr->set_eye(0, 0.1, 2.0);
	pinhole_ptr->set_lookat(0, 0.1, 0);
	pinhole_ptr->set_view_distance(1600);
	pinhole_ptr->compute_uvw();
	set_camera(pinhole_ptr);

	PointLight* light_ptr1 = new PointLight;
	light_ptr1->set_location(13, 10, 10);
	light_ptr1->scale_radiance(3.0);
    light_ptr1->set_cast_shadow(true);
	add_light(light_ptr1);
#endif // 0
#if 0 // 300*300: Horse97K
	pinhole_ptr->set_eye(0, 0.1, 6.0);
	pinhole_ptr->set_lookat(0, 0.1, 0);
	pinhole_ptr->set_view_distance(1600);
	pinhole_ptr->compute_uvw();
	set_camera(pinhole_ptr);

	PointLight* light_ptr1 = new PointLight;
	light_ptr1->set_location(13, 10, 10);
	light_ptr1->scale_radiance(3.0);
    light_ptr1->set_cast_shadow(true);
	add_light(light_ptr1);
#endif // 0
#if 1 // 400*400: hand
	pinhole_ptr->set_eye(25, 10, 25);
	pinhole_ptr->set_lookat(1, 1, 0);
	pinhole_ptr->set_view_distance(1600);
	pinhole_ptr->compute_uvw();
	set_camera(pinhole_ptr);

	PointLight* light_ptr1 = new PointLight;
	light_ptr1->set_location(13, 10, 10);
	light_ptr1->scale_radiance(3.0);
    light_ptr1->set_cast_shadow(true);
	add_light(light_ptr1);
#endif // 0
#if 0 // 400*400: goldfish_high_res,
	pinhole_ptr->set_eye(75, 20, 80);
	pinhole_ptr->set_lookat(-0.05, -0.5, 0);
	pinhole_ptr->set_view_distance(1600);
	pinhole_ptr->compute_uvw();
	set_camera(pinhole_ptr);

	PointLight* light_ptr1 = new PointLight;
	light_ptr1->set_location(13, 10, 10);
	light_ptr1->scale_radiance(3.0);
    light_ptr1->set_cast_shadow(true);
	add_light(light_ptr1);
#endif // 1

	Phong* phong_ptr1 = new Phong;
	phong_ptr1->set_ka(0.4);
	phong_ptr1->set_kd(0.8);
	phong_ptr1->set_cd(1.0, 0.2, 0.0);
	phong_ptr1->set_ks(0.5);
//	phong_ptr1->set_cs(1.0, 1.0, 0.0);
	phong_ptr1->set_exp(50.0);

//	char* file_name = ".\\PLYFiles\\goldfish_low_res.ply";
//	char* file_name = ".\\PLYFiles\\goldfish_high_res.ply";
//	char* file_name = ".\\PLYFiles\\Horse2K.ply";
//	char* file_name = ".\\PLYFiles\\Horse97K.ply";
//	char* file_name = ".\\PLYFiles\\Bunny16K.ply";
//	char* file_name = ".\\PLYFiles\\Bunny69K.ply";
//	char* file_name = ".\\PLYFiles\\dragon.ply";
//	char* file_name = ".\\PLYFiles\\happy.ply";
	char* file_name = ".\\PLYFiles\\hand.ply";
	Grid* grid_ptr = new Grid(new Mesh);
	grid_ptr->read_flat_triangles(file_name);
//	grid_ptr->read_smooth_triangles(file_name);
	grid_ptr->set_material(phong_ptr1);
	grid_ptr->setup_cells();
	add_object(grid_ptr);

	Phong* phong_ptr2 = new Phong;
	phong_ptr2->set_ka(0.4);
	phong_ptr2->set_kd(0.8);
	phong_ptr2->set_cd(0.5, 1.0, 0.5);
	phong_ptr2->set_ks(0.5);
//	phong_ptr2->set_cs(1.0, 1.0, 0.0);
	phong_ptr2->set_exp(50.0);

    Plane* plane_ptr1 = new Plane(Point3D(0, -2.0, 0), Normal(0, 1, 0));
	plane_ptr1->set_material(phong_ptr2);
	add_object(plane_ptr1);

}

该函数当前生成的图形是hand skeleton:

 

如果要生成其他图形:horse、bunny、goldfish、dragon、happy buddha。则需要调节各自对应的:下方绿色平面的位置、点光源的位置、lookfrom的位置、lookat的位置,以便从各自最佳的角度观测到图形及其阴影。

 

下方贴图是没有下方绿色平面,没有阴影时的图形。

顺序如下:















 

81.7 其他说明

 

完整的代码,参考:http://download.csdn.net/detail/libing_zeng/9776662

 

各种经典图形的PLY文件(包含Unix/Mac、Windows系统下的两种格式):

http://download.csdn.net/detail/libing_zeng/9775786

 

 

Referrance

[1]. Kevin Suffern, Ray Tracing from theGround Up, A K Peters Ltd, 2007.

[2]. http://www.cc.gatech.edu/projects/large_models/


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Q81:“三角形网格”之“PLY文件” 的相关文章

  • 访问私人成员[关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 通过将类的私有成员转换为 void 指针 然后转换为结构来访问类的私有成员是否合适 我认为我无权修改包含我需要访问的数据成员的类 如果不道德 我
  • 当我使用“control-c”关闭发送对等方的套接字时,为什么接收对等方的套接字不断接收“”

    我是套接字编程的新手 我知道使用 control c 关闭套接字是一个坏习惯 但是为什么在我使用 control c 关闭发送进程后 接收方上的套接字不断接收 在 control c 退出进程后 发送方的套接字不应该关闭吗 谢谢 我知道使用
  • 获取按下的按钮的返回值

    我有一个在特定事件中弹出的表单 它从数组中提取按钮并将标签值设置为特定值 因此 如果您要按下或单击此按钮 该函数应返回标签值 我怎样才能做到这一点 我如何知道点击了哪个按钮 此时代码返回 DialogResult 但我想从函数返回 Tag
  • pthread_cond_timedwait() 和 pthread_cond_broadcast() 解释

    因此 我在堆栈溢出和其他资源上进行了大量搜索 但我无法理解有关上述函数的一些内容 具体来说 1 当pthread cond timedwait 因为定时器值用完而返回时 它如何自动重新获取互斥锁 互斥锁可能被锁定在其他地方 例如 在生产者
  • 未解决的包含:“cocos2d.h” - Cocos2dx

    当我在 Eclipse 中导入 cocos2dx android 项目时 我的头文件上收到此警告 Unresolved inclusion cocos2d h 为什么是这样 它实际上困扰着我 该项目可以正确编译并运行 但我希望这种情况消失
  • 为什么#pragma optimize("", off)

    我正在审查一个 C MFC 项目 在某些文件的开头有这样一行 pragma optimize off 我知道这会关闭所有以下功能的优化 但这样做的动机通常是什么 我专门使用它来在一组特定代码中获得更好的调试信息 并在优化的情况下编译应用程序
  • Json.NET - 反序列化接口属性引发错误“类型是接口或抽象类,无法实例化”

    我有一个类 其属性是接口 public class Foo public int Number get set public ISomething Thing get set 尝试反序列化Foo使用 Json NET 的类给我一条错误消息
  • vector 超出范围后不清除内存

    我遇到了以下问题 我不确定我是否错了或者它是一个非常奇怪的错误 我填充了一个巨大的字符串数组 并希望在某个点将其清除 这是一个最小的例子 include
  • C# 中的递归自定义配置

    我正在尝试创建一个遵循以下递归结构的自定义配置部分
  • 在数据库中搜索时忽略空文本框

    此代码能够搜索数据并将其加载到DataGridView基于搜索表单文本框中提供的值 如果我将任何文本框留空 则不会有搜索结果 因为 SQL 查询是用 AND 组合的 如何在搜索 从 SQL 查询或 C 代码 时忽略空文本框 private
  • Github Action 在运行可执行文件时卡住

    我正在尝试设置运行google tests on a C repository using Github Actions正在运行的Windows Latest 构建过程完成 但是当运行测试时 它被卡住并且不执行从生成的可执行文件Visual
  • Qt表格小部件,删除行的按钮

    我有一个 QTableWidget 对于所有行 我将一列的 setCellWidget 设置为按钮 我想将此按钮连接到删除该行的函数 我尝试了这段代码 它不起作用 因为如果我只是单击按钮 我不会将当前行设置为按钮的行 ui gt table
  • 插入记录后如何从SQL Server获取Identity值

    我在数据库中添加一条记录identity价值 我想在插入后获取身份值 我不想通过存储过程来做到这一点 这是我的代码 SQLString INSERT INTO myTable SQLString Cal1 Cal2 Cal3 Cal4 SQ
  • 控制到达非 void 函数末尾 -wreturn-type

    这是查找四个数字中的最大值的代码 include
  • 如何让Gtk+窗口背景透明?

    我想让 Gtk 窗口的背景透明 以便只有窗口中的小部件可见 我找到了一些教程 http mikehearn wordpress com 2006 03 26 gtk windows with alpha channels https web
  • 32 位到 64 位内联汇编移植

    我有一段 C 代码 在 GNU Linux 环境下用 g 编译 它加载一个函数指针 它如何执行并不重要 使用一些内联汇编将一些参数推送到堆栈上 然后调用该函数 代码如下 unsigned long stack 1 23 33 43 save
  • mysql-connector-c++ - “get_driver_instance”不是“sql::mysql”的成员

    我是 C 的初学者 我认为学习的唯一方法就是接触一些代码 我正在尝试构建一个连接到 mysql 数据库的程序 我在 Linux 上使用 g 没有想法 我运行 make 这是我的错误 hello cpp 38 error get driver
  • ASP.NET MVC 6 (ASP.NET 5) 中的 Application_PreSendRequestHeaders 和 Application_BeginRequest

    如何在 ASP NET 5 MVC6 中使用这些方法 在 MVC5 中 我在 Global asax 中使用了它 现在呢 也许是入门班 protected void Application PreSendRequestHeaders obj
  • 防止索引超出范围错误

    我想编写对某些条件的检查 而不必使用 try catch 并且我想避免出现 Index Out of Range 错误的可能性 if array Element 0 Object Length gt 0 array Element 1 Ob
  • 使用 libcurl 检查 SFTP 站点上是否存在文件

    我使用 C 和 libcurl 进行 SFTP FTPS 传输 在上传文件之前 我需要检查文件是否存在而不实际下载它 如果该文件不存在 我会遇到以下问题 set up curlhandle for the public private ke

随机推荐

  • mysql常用命令有什么

    MySQL 数据库常用命令 1 MySQL常用命令 create database name 创建数据库 use databasename 选择数据库 drop database name 直接删除数据库 不提醒 show tables 显
  • 神经网络预测彩票数据

    一 人工智能深度学习神经网络在双色球彩票中的应用研究 一 https www cnblogs com zdz8207 p DeepLearning NeuralNetworks html 二 百度AI http ai baidu com p
  • js逆向-某399游戏登陆参数

    声明 本文仅供学习参考 禁止用于其他途径 违者后果自负 前言 目标网站 aHR0cHM6Ly93d3cuNDM5OS5jb20v 登陆接口 aHR0cHM6Ly9wdGxvZ2luLjQzOTkuY29tL3B0bG9naW4vbG9naW
  • 基于树莓派4B设计的智能家居系统(华为云IOT)

    基于树莓派的智能家居控制系统 华为云IOT 一 设计需求 前言 本次设计实现了一个基于树莓派的智能家居系统 可以对家庭环境进行实时监测和控制 提高居家安全性和舒适度 该系统采用了多种传感器和模块 包括温湿度传感器 烟雾传感器 火焰传感器 光
  • virtualenv: error: unrecognized arguments: --no-site-packages

    使用virtualenv version 看到自己的版本大于20 就可以将如下这段删除 export VIRTUALENVWRAPPER VIRTUALENV ARGS no site packages 我碰到的情况下 user makef
  • 机器学习算法之决策树

    原文 http www jianshu com p 6eecdeee5012 决策树是一种简单高效并且具有强解释性的模型 广泛应用于数据分析领域 其本质是一颗由多个判断节点组成的树 如 决策树 在使用模型进行预测时 根据输入参数依次在各个判
  • Go语言网络编程(socket编程)WebSocket编程

    1 WebSocket编程 1 1 1 webSocket是什么 WebSocket是一种在单个TCP连接上进行全双工通信的协议 WebSocket使得客户端和服务器之间的数据交换变得更加简单 允许服务端主动向客户端推送数据 在WebSoc
  • 关于epoll的IO模型是同步异步的一次纠结过程

    这篇文章的结论就是epoll属于同步非阻塞模型 这个东西貌似目前还是有争议 在新的2 6内核之后 epoll应该属于异步io的范围了 golang的高并发特性就是底层封装了epoll模型的函数 但也有文章指出epoll属于 伪AIO 真正的
  • IOS之同步请求、异步请求、GET请求、POST请求

    1 同步请求可以从因特网请求数据 一旦发送同步请求 程序将停止用户交互 直至服务器返回数据完成 才可以进行下一步操作 2 异步请求不会阻塞主线程 而会建立一个新的线程来操作 用户发出异步请求后 依然可以对UI进行操作 程序可以继续运行 3
  • PyQt5 笔记5 -- 消息框(QMessageBox)

    PyQt5 笔记5 消息框 QMessageBox 1 常用函数 函数原型 信息框 QMessageBox information self 框名 内容 按钮s 默认按钮 问答框 QMessageBox question self 框名 内
  • 西门子PLC学习笔记十-(计数器)

    S7 300 400的计数器一般占两个字节 是16位的 CPU最多可以使用64 512个计数器 计数器地址编号为C0 C511 1 S CUD 加减计数器 加减计数器波形图 2 S CU 加计数器 3 S CD 减计数器 4 三种计数器对应
  • Unity制作多屏幕解决方案

    最近制作了一个多屏幕的项目 多屏幕指的是一个电脑主机 连接多个显示器 我这个项目使用了一个显卡连接了三个显示设备 Unity UGUI提供Canvas画布 在画布上有一个TargetDisplay的解决解决方案 Canvas结合Camera
  • 摸不着头脑,flatMap处理后居然无法去重(原来是数据库添加字段的时候多了个空格= =)

    前言 这应该是一个bug 这是一篇毫无营养的博客 当我正在尝试从页面中获取作者时 发现之前应该被Set包装的作者字符串居然发生了重复 于是我赶紧回到源码处 并加了条log日志 开始排查问题 我的代码是这样的 查作者 绝对也会有 Set
  • 死锁产生的四个必要条件(缺一不可)

    死锁产生必须同时满足四个条件 只要其中任意一条不成立 死锁就不会发生 1 互斥条件 进程要求对所分配的资源进行排他性控制 即在一段时间内某项资源只被 一个进程所占有 此时若有其他进程请求该资源 则请求进程只能等待 如图一 2 非抢占 进程所
  • 解决OptiSystem安装、使用过程中遇到的问题

    系统环境 Win10系统 问题1 在上一篇文章 Optisystem7 0安装教程 Win10系统 中提到 安装过程中会弹出一个对话框 需要点击 忽略 但是安装过程中出现下图错误 错误代码 0x3 点击忽略 仍然会继续弹出这个对话框 或者第
  • Flink实战-(1)Flink-CDC MySQL同步到MySQL(select)

    背景 基于select语句的Flink CDC 适用于数据同步的全量同步的场景 可以结合 Azkaban 或者dolphin scheduler 做定时调度 T 1 数据同步 1 maven
  • Verdi之nTrace/nSchema

    目录 3 nTrace介绍 3 1 启动Verdi 3 2查看Verdi中的设计结构 3 3查看Verdi中的验证结构 3 4 查找模块和trace信号 3 5 查找string 3 6 信号drive load 3 7 快速查看设计有哪些
  • 使用 javascript 将鼠标指针移动到特定位置

    请注意 无法将鼠标指针移动到 JavaScript 中的特定位置 主要原因是它会给用户带来安全问题并损害用户体验 在这篇文章中 我们将创建一个假的或自定义的鼠标指针 它可能看起来类似于默认系统的鼠标指针 然后我们将使用 JavaScript
  • 使用TensorBoard可视化模型

    为了了解发生的情况 我们在模型训练期间打印一些统计数据 以了解训练是否在进行中 但是 我们可以做得更好 PyTorch 与 TensorBoard 集成在一起 TensorBoard 是一种工具 用于可视化神经网络训练运行的结果 读取数据并
  • Q81:“三角形网格”之“PLY文件”

    81 1 引入 在 Q79 和 Q80 中用三角形网格细分曲面时 都是将每一个三角形的三个顶点的坐标都保存在内存中 这句话有两个重点 其一 每个三角形的三个顶点的坐标都计算了一次 但是 每个顶点都是被好几个三角形公用的 所以每个顶点的坐标被