00001 #include <agile_grasp/plot.h>
00002
00003
00004 void Plot::plotFingers(const std::vector<Handle>& handle_list, const PointCloud::Ptr& cloud,
00005 std::string str, double outer_diameter, double hand_depth)
00006 {
00007 const int WIDTH = pcl::visualization::PCL_VISUALIZER_LINE_WIDTH;
00008
00009 boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer = createViewer(str);
00010
00011 addCloudToViewer(viewer, cloud, "cloud");
00012
00013 std::vector<GraspHypothesis> hand_list;
00014 std::vector<GraspHypothesis> center_hand_list;
00015 for (int i = 0; i < handle_list.size(); i++)
00016 {
00017 const std::vector<int>& inliers = handle_list[i].getInliers();
00018 const std::vector<GraspHypothesis>& hands = handle_list[i].getHandList();
00019 GraspHypothesis center_hand(handle_list[i].getAxis(), handle_list[i].getApproach(), handle_list[i].getBinormal(),
00020 handle_list[i].getCenter(), handle_list[i].getCenter(), 0.0, Eigen::Matrix3Xd::Zero(3,0), std::vector<int>(),
00021 std::vector<int>(), 0);
00022 center_hand_list.push_back(center_hand);
00023
00024 for (int j = 0; j < inliers.size(); j++)
00025 {
00026 hand_list.push_back(hands[inliers[j]]);
00027 }
00028 }
00029
00030 PointCloud::Ptr cloud_fingers(new PointCloud);
00031 cloud_fingers = createFingersCloud(hand_list, outer_diameter, hand_depth);
00032 addCloudToViewer(viewer, cloud_fingers, "fingers", 3);
00033
00034 PointCloud::Ptr center_cloud_fingers(new PointCloud);
00035 center_cloud_fingers = createFingersCloud(center_hand_list, outer_diameter, hand_depth);
00036 addCloudToViewer(viewer, center_cloud_fingers, "center_fingers", 3);
00037
00038 runViewer(viewer);
00039 }
00040
00041
00042 void Plot::plotFingers(const std::vector<GraspHypothesis>& hand_list, const PointCloud::Ptr& cloud,
00043 std::string str, double outer_diameter, double hand_depth)
00044 {
00045 const int WIDTH = pcl::visualization::PCL_VISUALIZER_LINE_WIDTH;
00046
00047 boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer = createViewer(str);
00048
00049 addCloudToViewer(viewer, cloud, "cloud");
00050
00051 PointCloud::Ptr cloud_fingers(new PointCloud);
00052 cloud_fingers = createFingersCloud(hand_list, outer_diameter, hand_depth);
00053 addCloudToViewer(viewer, cloud_fingers, "fingers", 3);
00054
00055 runViewer(viewer);
00056 }
00057
00058
00059 PointCloud::Ptr Plot::createFingersCloud(const std::vector<GraspHypothesis>& hand_list,
00060 double outer_diameter, double hand_depth)
00061 {
00062 PointCloud::Ptr cloud_fingers(new PointCloud);
00063
00064 for (int i = 0; i < hand_list.size(); i++)
00065 {
00066 Eigen::Vector3d bottom = hand_list[i].getGraspBottom() - hand_depth * hand_list[i].getApproach();
00067 pcl::PointXYZRGBA pc = eigenVector3dToPointXYZRGBA(bottom);
00068 setPointColor(hand_list[i], pc);
00069
00070 double width = outer_diameter;
00071 double hw = 0.5 * width;
00072 double step = hw / 30.0;
00073 Eigen::Vector3d left_bottom = bottom + hw * hand_list[i].getBinormal();
00074 Eigen::Vector3d right_bottom = bottom - hw * hand_list[i].getBinormal();
00075 pcl::PointXYZRGBA p1 = eigenVector3dToPointXYZRGBA(left_bottom);
00076 pcl::PointXYZRGBA p2 = eigenVector3dToPointXYZRGBA(right_bottom);
00077 setPointColor(hand_list[i], p1);
00078 setPointColor(hand_list[i], p2);
00079 cloud_fingers->points.push_back(pc);
00080 cloud_fingers->points.push_back(p1);
00081 cloud_fingers->points.push_back(p2);
00082
00083
00084 for (double j=step; j < hw; j+=step)
00085 {
00086 Eigen::Vector3d lb, rb, a;
00087 lb = bottom + j * hand_list[i].getBinormal();
00088 rb = bottom - j * hand_list[i].getBinormal();
00089 a = bottom - j * hand_list[i].getApproach();
00090 pcl::PointXYZRGBA plb = eigenVector3dToPointXYZRGBA(lb);
00091 setPointColor(hand_list[i], plb);
00092 pcl::PointXYZRGBA prb = eigenVector3dToPointXYZRGBA(rb);
00093 setPointColor(hand_list[i], prb);
00094 pcl::PointXYZRGBA pa = eigenVector3dToPointXYZRGBA(a);
00095 setPointColor(hand_list[i], pa);
00096 cloud_fingers->points.push_back(plb);
00097 cloud_fingers->points.push_back(prb);
00098 cloud_fingers->points.push_back(pa);
00099 }
00100
00101
00102 double dist = 0.06;
00103 step = dist / 40.0;
00104 for (double j=step; j < dist; j+=step)
00105 {
00106 Eigen::Vector3d lt, rt;
00107 lt = left_bottom + j * hand_list[i].getApproach();
00108 rt = right_bottom + j * hand_list[i].getApproach();
00109 pcl::PointXYZRGBA plt = eigenVector3dToPointXYZRGBA(lt);
00110 setPointColor(hand_list[i], plt);
00111 pcl::PointXYZRGBA prt = eigenVector3dToPointXYZRGBA(rt);
00112 setPointColor(hand_list[i], prt);
00113 cloud_fingers->points.push_back(plt);
00114 cloud_fingers->points.push_back(prt);
00115 }
00116 }
00117
00118 return cloud_fingers;
00119 }
00120
00121
00122 void Plot::plotHands(const std::vector<GraspHypothesis>& hand_list,
00123 const std::vector<GraspHypothesis>& antipodal_hand_list, const PointCloud::Ptr& cloud, std::string str,
00124 bool use_grasp_bottom)
00125 {
00126 PointCloudNormal::Ptr hands_cloud = createNormalsCloud(hand_list, false, false);
00127 PointCloudNormal::Ptr antipodal_hands_cloud = createNormalsCloud(antipodal_hand_list, true,
00128 false);
00129 plotHandsHelper(hands_cloud, antipodal_hands_cloud, cloud, str, use_grasp_bottom);
00130 }
00131
00132
00133 void Plot::plotHands(const std::vector<GraspHypothesis>& hand_list, const PointCloud::Ptr& cloud,
00134 std::string str, bool use_grasp_bottom)
00135 {
00136 PointCloudNormal::Ptr hands_cloud = createNormalsCloud(hand_list, false, false);
00137 PointCloudNormal::Ptr antipodal_hands_cloud = createNormalsCloud(hand_list, true, false);
00138 plotHandsHelper(hands_cloud, antipodal_hands_cloud, cloud, str, use_grasp_bottom);
00139 }
00140
00141
00142 void Plot::plotSamples(const std::vector<int>& index_list, const PointCloud::Ptr& cloud)
00143 {
00144 PointCloud::Ptr samples_cloud(new PointCloud);
00145 for (int i = 0; i < index_list.size(); i++)
00146 samples_cloud->points.push_back(cloud->points[index_list[i]]);
00147
00148 boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer = createViewer("Samples");
00149 addCloudToViewer(viewer, cloud, "cloud");
00150 addCloudToViewer(viewer, samples_cloud, "samples");
00151 viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 1.0, 0.0, 0.0, "samples");
00152
00153 runViewer(viewer);
00154 }
00155
00156
00157 void Plot::plotLocalAxes(const std::vector<Quadric>& quadric_list, const PointCloud::Ptr& cloud)
00158 {
00159 boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer = createViewer("Local Axes");
00160 addCloudToViewer(viewer, cloud, "cloud");
00161
00162 for (int i = 0; i < quadric_list.size(); i++)
00163 quadric_list[i].plotAxes((void*) &viewer, i);
00164
00165 runViewer(viewer);
00166 }
00167
00168
00169 void Plot::plotCameraSource(const Eigen::VectorXi& pts_cam_source_in, const PointCloud::Ptr& cloud)
00170 {
00171 PointCloud::Ptr left_cloud(new PointCloud);
00172 PointCloud::Ptr right_cloud(new PointCloud);
00173
00174 for (int i = 0; i < pts_cam_source_in.size(); i++)
00175 {
00176 if (pts_cam_source_in(i) == 0)
00177 left_cloud->points.push_back(cloud->points[i]);
00178 else if (pts_cam_source_in(i) == 1)
00179 right_cloud->points.push_back(cloud->points[i]);
00180 }
00181
00182 boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer = createViewer("Camera Sources");
00183 addCloudToViewer(viewer, left_cloud, "left_cloud");
00184 addCloudToViewer(viewer, right_cloud, "right_cloud");
00185 runViewer(viewer);
00186 }
00187
00188
00189 PointCloudNormal::Ptr Plot::createNormalsCloud(
00190 const std::vector<GraspHypothesis>& hand_list, bool plots_only_antipodal, bool plots_grasp_bottom)
00191 {
00192 PointCloudNormal::Ptr cloud(new PointCloudNormal);
00193
00194 for (int i = 0; i < hand_list.size(); i++)
00195 {
00196 Eigen::Matrix3Xd grasp_surface = hand_list[i].getGraspSurface();
00197 Eigen::Matrix3Xd grasp_bottom = hand_list[i].getGraspBottom();
00198 Eigen::Matrix3Xd hand_approach = hand_list[i].getApproach();
00199
00200 if (!plots_only_antipodal || (plots_only_antipodal && hand_list[i].isFullAntipodal()))
00201 {
00202 pcl::PointNormal p;
00203 if (!plots_grasp_bottom)
00204 {
00205 p.x = grasp_surface(0);
00206 p.y = grasp_surface(1);
00207 p.z = grasp_surface(2);
00208 }
00209 else
00210 {
00211 p.x = grasp_bottom(0);
00212 p.y = grasp_bottom(1);
00213 p.z = grasp_bottom(2);
00214 }
00215 p.normal[0] = -hand_approach(0);
00216 p.normal[1] = -hand_approach(1);
00217 p.normal[2] = -hand_approach(2);
00218 cloud->points.push_back(p);
00219 }
00220 }
00221
00222 return cloud;
00223 }
00224
00225
00226 void Plot::addCloudToViewer(boost::shared_ptr<pcl::visualization::PCLVisualizer>& viewer,
00227 const PointCloud::Ptr& cloud, const std::string& name, int point_size)
00228 {
00229 pcl::visualization::PointCloudColorHandlerRGBField<pcl::PointXYZRGBA> rgb(cloud);
00230 viewer->addPointCloud<pcl::PointXYZRGBA>(cloud, rgb, name);
00231 viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, point_size, name);
00232 }
00233
00234
00235 void Plot::addCloudNormalsToViewer(boost::shared_ptr<pcl::visualization::PCLVisualizer>& viewer,
00236 const PointCloudNormal::Ptr& cloud, double line_width, double* color_cloud,
00237 double* color_normals, const std::string& cloud_name, const std::string& normals_name)
00238 {
00239 viewer->addPointCloud<pcl::PointNormal>(cloud, cloud_name);
00240 viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, color_cloud[0],
00241 color_cloud[1], color_cloud[2], cloud_name);
00242 viewer->addPointCloudNormals<pcl::PointNormal>(cloud, 1, 0.04, normals_name);
00243 viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_LINE_WIDTH, line_width,
00244 normals_name);
00245 viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, color_normals[0],
00246 color_normals[1], color_normals[2], normals_name);
00247 }
00248
00249
00250 void Plot::plotHandsHelper(const PointCloudNormal::Ptr& hands_cloud,
00251 const PointCloudNormal::Ptr& antipodal_hands_cloud, const PointCloud::Ptr& cloud,
00252 std::string str, bool use_grasp_bottom)
00253 {
00254 std::cout << "Drawing " << hands_cloud->size() << " grasps of which " << antipodal_hands_cloud->size()
00255 << " are antipodal grasps.\n";
00256
00257 std::string title = "Hands: " + str;
00258 boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer = createViewer(title);
00259 addCloudToViewer(viewer, cloud, "cloud");
00260
00261 double green[3] = { 0.0, 1.0, 0.0 };
00262 double cyan[3] = { 0.0, 0.4, 0.8 };
00263 addCloudNormalsToViewer(viewer, hands_cloud, 2, green, cyan, std::string("hands"),
00264 std::string("approaches"));
00265
00266 if (antipodal_hands_cloud->size() > 0)
00267 {
00268 double red[3] = { 1.0, 0.0, 0.0 };
00269 addCloudNormalsToViewer(viewer, antipodal_hands_cloud, 2, green, red, std::string("antipodal_hands"),
00270 std::string("antipodal_approaches"));
00271 }
00272
00273 runViewer(viewer);
00274 }
00275
00276
00277 void Plot::runViewer(boost::shared_ptr<pcl::visualization::PCLVisualizer>& viewer)
00278 {
00279
00280 viewer->initCameraParameters();
00281 viewer->setPosition(0, 0);
00282 viewer->setSize(640, 480);
00283
00284 while (!viewer->wasStopped())
00285 {
00286 viewer->spinOnce(100);
00287 boost::this_thread::sleep(boost::posix_time::microseconds(100000));
00288 }
00289
00290 viewer->close();
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300 }
00301
00302
00303 boost::shared_ptr<pcl::visualization::PCLVisualizer> Plot::createViewer(std::string title)
00304 {
00305 boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer(title));
00306 viewer->setBackgroundColor(1, 1, 1);
00307 return viewer;
00308 }
00309
00310
00311 void Plot::createVisualPublishers(ros::NodeHandle& node, double marker_lifetime)
00312 {
00313 hypotheses_pub_ = node.advertise<visualization_msgs::MarkerArray>("grasp_hypotheses_visual", 10);
00314 antipodal_pub_ = node.advertise<visualization_msgs::MarkerArray>("antipodal_grasps_visual", 10);
00315 handles_pub_ = node.advertise<visualization_msgs::MarkerArray>("handles_visual", 10);
00316 marker_lifetime_ = marker_lifetime;
00317 }
00318
00319 void Plot::plotGraspsRviz(const std::vector<GraspHypothesis>& hand_list, const std::string& frame, bool is_antipodal)
00320 {
00321 double red[3] = {1, 0, 0};
00322 double cyan[3] = {0, 1, 1};
00323 double* color;
00324 if (is_antipodal)
00325 {
00326 color = red;
00327 std::cout << "Visualizing antipodal grasps in Rviz ...\n";
00328 }
00329 else
00330 {
00331 color = cyan;
00332 std::cout << "Visualizing grasp hypotheses in Rviz ...\n";
00333 }
00334
00335 visualization_msgs::MarkerArray marker_array;
00336 marker_array.markers.resize(hand_list.size());
00337
00338 for (int i=0; i < hand_list.size(); i++)
00339 {
00340 geometry_msgs::Point position;
00341 position.x = hand_list[i].getGraspSurface()(0);
00342 position.y = hand_list[i].getGraspSurface()(1);
00343 position.z = hand_list[i].getGraspSurface()(2);
00344 visualization_msgs::Marker marker = createApproachMarker(frame, position, hand_list[i].getApproach(), i, color, 0.4,
00345 0.004);
00346 marker.ns = "grasp_hypotheses";
00347 marker.id = i;
00348 marker_array.markers[i] = marker;
00349 }
00350
00351 if (is_antipodal)
00352 antipodal_pub_.publish(marker_array);
00353 else
00354 hypotheses_pub_.publish(marker_array);
00355
00356 ros::Duration(1.0).sleep();
00357 }
00358
00359
00360 void Plot::plotHandlesRviz(const std::vector<Handle>& handle_list, const std::string& frame)
00361 {
00362 std::cout << "Visualizing handles in Rviz ...\n";
00363 double green[3] = {0, 1, 0};
00364 visualization_msgs::MarkerArray marker_array;
00365 marker_array.markers.resize(handle_list.size());
00366
00367 for (int i=0; i < handle_list.size(); i++)
00368 {
00369 geometry_msgs::Point position;
00370 position.x = handle_list[i].getHandsCenter()(0);
00371 position.y = handle_list[i].getHandsCenter()(1);
00372 position.z = handle_list[i].getHandsCenter()(2);
00373 visualization_msgs::Marker marker = createApproachMarker(frame, position, handle_list[i].getApproach(), i, green, 0.6,
00374 0.008);
00375 marker.ns = "handles";
00376 marker_array.markers[i] = marker;
00377 }
00378
00379 handles_pub_.publish(marker_array);
00380 ros::Duration(1.0).sleep();
00381 }
00382
00383
00384 void Plot::plotHandles(const std::vector<Handle>& handle_list, const PointCloud::Ptr& cloud, std::string str)
00385 {
00386 double colors[10][3] = { { 0, 0.4470, 0.7410 }, { 0.8500, 0.3250, 0.0980 }, { 0.9290, 0.6940, 0.1250 }, {
00387 0.4940, 0.1840, 0.5560 }, { 0.4660, 0.6740, 0.1880 }, { 0.3010, 0.7450, 0.9330 }, { 0.6350, 0.0780,
00388 0.1840 }, { 0, 0.4470, 0.7410 }, { 0.8500, 0.3250, 0.0980 }, { 0.9290, 0.6940, 0.1250} };
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402 std::vector<pcl::PointCloud<pcl::PointNormal>::Ptr> clouds;
00403 pcl::PointCloud<pcl::PointNormal>::Ptr handle_cloud(new pcl::PointCloud<pcl::PointNormal>());
00404
00405 for (int i = 0; i < handle_list.size(); i++)
00406 {
00407 pcl::PointNormal p;
00408 p.x = handle_list[i].getHandsCenter()(0);
00409 p.y = handle_list[i].getHandsCenter()(1);
00410 p.z = handle_list[i].getHandsCenter()(2);
00411 p.normal[0] = -handle_list[i].getApproach()(0);
00412 p.normal[1] = -handle_list[i].getApproach()(1);
00413 p.normal[2] = -handle_list[i].getApproach()(2);
00414 handle_cloud->points.push_back(p);
00415
00416 const std::vector<int>& inliers = handle_list[i].getInliers();
00417 const std::vector<GraspHypothesis>& hand_list = handle_list[i].getHandList();
00418 pcl::PointCloud<pcl::PointNormal>::Ptr axis_cloud(new pcl::PointCloud<pcl::PointNormal>);
00419
00420 for (int j = 0; j < inliers.size(); j++)
00421 {
00422 pcl::PointNormal p;
00423 p.x = hand_list[inliers[j]].getGraspSurface()(0);
00424 p.y = hand_list[inliers[j]].getGraspSurface()(1);
00425 p.z = hand_list[inliers[j]].getGraspSurface()(2);
00426 p.normal[0] = -hand_list[inliers[j]].getApproach()(0);
00427 p.normal[1] = -hand_list[inliers[j]].getApproach()(1);
00428 p.normal[2] = -hand_list[inliers[j]].getApproach()(2);
00429 axis_cloud->points.push_back(p);
00430 }
00431 clouds.push_back(axis_cloud);
00432 }
00433
00434 std::string title = "Handles: " + str;
00435 boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer(title));
00436 viewer->setBackgroundColor(1, 1, 1);
00437
00438
00439 addCloudToViewer(viewer, cloud, "cloud");
00440
00441 for (int i = 0; i < clouds.size(); i++)
00442 {
00443 std::string name = "handle_" + boost::lexical_cast<std::string>(i);
00444 int ci = i % 6;
00445
00446 viewer->addPointCloud<pcl::PointNormal>(clouds[i], name);
00447 viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, colors[ci][0],
00448 colors[ci][1], colors[ci][2], name);
00449 std::string name2 = "approach_" + boost::lexical_cast<std::string>(i);
00450 viewer->addPointCloudNormals<pcl::PointNormal>(clouds[i], 1, 0.04, name2);
00451 viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_LINE_WIDTH, 2, name2);
00452 viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, colors[ci][0],
00453 colors[ci][1], colors[ci][2], name2);
00454 }
00455
00456 viewer->addPointCloud<pcl::PointNormal>(handle_cloud, "handles");
00457 viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 0, 0, 0, "handles");
00458 viewer->addPointCloudNormals<pcl::PointNormal>(handle_cloud, 1, 0.08, "approach");
00459 viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_LINE_WIDTH, 4, "approach");
00460 viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 0, 0, 0, "approach");
00461
00462
00463 viewer->initCameraParameters();
00464 viewer->setPosition(0, 0);
00465 viewer->setSize(640, 480);
00466
00467 while (!viewer->wasStopped())
00468 {
00469 viewer->spinOnce(100);
00470 boost::this_thread::sleep(boost::posix_time::microseconds(100000));
00471 }
00472
00473 viewer->close();
00474 }
00475
00476
00477 visualization_msgs::Marker Plot::createApproachMarker(const std::string& frame, const geometry_msgs::Point& center,
00478 const Eigen::Vector3d& approach, int id, const double* color, double alpha, double diam)
00479 {
00480 visualization_msgs::Marker marker = createMarker(frame);
00481 marker.type = visualization_msgs::Marker::ARROW;
00482 marker.id = id;
00483 marker.scale.x = diam;
00484 marker.scale.y = diam;
00485 marker.scale.z = 0.01;
00486 marker.color.r = color[0];
00487 marker.color.g = color[1];
00488 marker.color.b = color[2];
00489 marker.color.a = alpha;
00490 geometry_msgs::Point p, q;
00491 p.x = center.x;
00492 p.y = center.y;
00493 p.z = center.z;
00494 q.x = p.x - 0.03 * approach(0);
00495 q.y = p.y - 0.03 * approach(1);
00496 q.z = p.z - 0.03 * approach(2);
00497 marker.points.push_back(p);
00498 marker.points.push_back(q);
00499 return marker;
00500 }
00501
00502
00503 visualization_msgs::Marker Plot::createMarker(const std::string& frame)
00504 {
00505 visualization_msgs::Marker marker;
00506 marker.header.frame_id = frame;
00507 marker.header.stamp = ros::Time::now();
00508 marker.lifetime = ros::Duration(marker_lifetime_);
00509 marker.action = visualization_msgs::Marker::ADD;
00510 return marker;
00511 }
00512
00513
00514 void Plot::setPointColor(const GraspHypothesis& hand, pcl::PointXYZRGBA& p)
00515 {
00516 p.a = 128;
00517
00518 if (hand.isFullAntipodal())
00519 {
00520 p.r = 0;
00521 p.g = 255;
00522 p.b = 0;
00523 }
00524 else
00525 {
00526 p.r = 255;
00527 p.g = 0;
00528 p.b = 0;
00529 }
00530 }
00531
00532
00533 pcl::PointXYZRGBA Plot::eigenVector3dToPointXYZRGBA(const Eigen::Vector3d& v)
00534 {
00535 pcl::PointXYZRGBA p;
00536 p.x = v(0);
00537 p.y = v(1);
00538 p.z = v(2);
00539 return p;
00540 }