|
1 | 1 | #include "ViewPanel3D.h" |
| 2 | +#include "HistoryManager.h" |
2 | 3 | #include "LayerInspectorDialog.h" |
3 | 4 | #include "ui_ViewPanel3D.h" |
4 | 5 | #include "GlobalUIModel.h" |
5 | 6 | #include "Generic3DModel.h" |
| 7 | +#include "Generic3DRenderer.h" |
6 | 8 | #include "itkCommand.h" |
7 | 9 | #include "IRISException.h" |
8 | 10 | #include "IRISApplication.h" |
@@ -71,6 +73,9 @@ ViewPanel3D::ViewPanel3D(QWidget *parent) : |
71 | 73 | m_DropMenu->addAction(ui->actionSave_Viewpoint); |
72 | 74 | m_DropMenu->addAction(ui->actionRestore_Viewpoint); |
73 | 75 | m_DropMenu->addSeparator(); |
| 76 | + m_DropMenu->addAction(ui->actionExport_Viewpoint); |
| 77 | + m_DropMenu->addAction(ui->actionImport_Viewpoint); |
| 78 | + m_DropMenu->addSeparator(); |
74 | 79 | m_DropMenu->addAction(ui->actionContinuous_Update); |
75 | 80 | ui->actionContinuous_Update->setObjectName("actionContinuousUpdate"); |
76 | 81 | m_DropMenu->addSeparator(); |
@@ -408,6 +413,190 @@ void ViewPanel3D::on_actionRestore_Viewpoint_triggered() |
408 | 413 | m_Model->RestoreCameraState(); |
409 | 414 | } |
410 | 415 |
|
| 416 | +template <unsigned int VDim> |
| 417 | +vnl_vector_fixed<double, VDim> |
| 418 | +read_json_array(QJsonObject &json, const char *field) |
| 419 | +{ |
| 420 | + vnl_vector_fixed<double, VDim> vec; |
| 421 | + QString error = |
| 422 | + QCoreApplication::translate("ViewPanel3D", "JSON error: %1: expected an array of %2 numbers") |
| 423 | + .arg(field) |
| 424 | + .arg(VDim); |
| 425 | + |
| 426 | + if(!json[field].isArray() || json[field].toArray().count() != VDim) |
| 427 | + throw error; |
| 428 | + |
| 429 | + for(unsigned int i = 0; i < VDim; ++i) |
| 430 | + { |
| 431 | + if(!json[field][i].isDouble()) |
| 432 | + throw error; |
| 433 | + vec[i] = json[field][i].toDouble(); |
| 434 | + } |
| 435 | + |
| 436 | + return vec; |
| 437 | +} |
| 438 | + |
| 439 | +double |
| 440 | +read_json_double(QJsonObject &json, const char *field) |
| 441 | +{ |
| 442 | + QString error = QCoreApplication::translate("ViewPanel3D", "JSON error: %1: expected a number").arg(field); |
| 443 | + |
| 444 | + if (!json[field].isDouble()) |
| 445 | + throw error; |
| 446 | + |
| 447 | + return json[field].toDouble(); |
| 448 | +} |
| 449 | + |
| 450 | +bool |
| 451 | +read_json_bool(QJsonObject &json, const char *field) |
| 452 | +{ |
| 453 | + QString error = QCoreApplication::translate("ViewPanel3D", "JSON error: %1: expected a boolean").arg(field); |
| 454 | + |
| 455 | + if (!json[field].isBool()) |
| 456 | + throw error; |
| 457 | + |
| 458 | + return json[field].isBool(); |
| 459 | +} |
| 460 | + |
| 461 | + |
| 462 | +void ViewPanel3D::LoadCameraViewpoint(QString file) |
| 463 | +{ |
| 464 | + QFile loadFile(file); |
| 465 | + if (!loadFile.open(QIODevice::ReadOnly)) |
| 466 | + QMessageBox::warning(this, "Import camera viewpoint error", |
| 467 | + QString("I/O error: ") + loadFile.errorString()); |
| 468 | + |
| 469 | + QByteArray loadData = loadFile.readAll(); |
| 470 | + QJsonParseError jsonError; |
| 471 | + QJsonDocument loadDoc(QJsonDocument::fromJson(loadData, &jsonError)); |
| 472 | + |
| 473 | + try |
| 474 | + { |
| 475 | + if (loadDoc.isNull()) |
| 476 | + throw tr("JSON error %1").arg(jsonError.errorString()); |
| 477 | + |
| 478 | + QJsonObject json_main = loadDoc.object(); |
| 479 | + |
| 480 | + if(!json_main["file_type"].isString() || json_main["file_type"].toString() != "itksnap_camera_viewpoint") |
| 481 | + throw tr("JSON error: 'file_type' field is missing or malformed"); |
| 482 | + |
| 483 | + if(!json_main["version"].isString()) |
| 484 | + throw tr("JSON error: 'version' field is missing"); |
| 485 | + |
| 486 | + if(!json_main["camera"].isObject()) |
| 487 | + throw tr("JSON error: 'camera' field is missing or not an object"); |
| 488 | + |
| 489 | + // Read the camera properties |
| 490 | + QJsonObject json_camera = json_main["camera"].toObject(); |
| 491 | + |
| 492 | + // QJsonArray to Vector3d |
| 493 | + Vector3d camPosition = read_json_array<3>(json_camera, "position"); |
| 494 | + Vector3d camFocalPoint = read_json_array<3>(json_camera, "focal_point"); |
| 495 | + Vector3d camViewUp = read_json_array<3>(json_camera, "view_up"); |
| 496 | + Vector2d camClippingRange = read_json_array<2>(json_camera, "clipping_range"); |
| 497 | + double camViewAngle = read_json_double(json_camera, "view_angle"); |
| 498 | + double camParallelScale = read_json_double(json_camera, "parallel_scale"); |
| 499 | + int camParallelProjection = read_json_bool(json_camera, "parallel_projection"); |
| 500 | + |
| 501 | + CameraState cam = { .position = camPosition, |
| 502 | + .focal_point = camFocalPoint, |
| 503 | + .view_up = camViewUp, |
| 504 | + .clipping_range = camClippingRange, |
| 505 | + .view_angle = camViewAngle, |
| 506 | + .parallel_scale = camParallelScale, |
| 507 | + .parallel_projection = camParallelProjection }; |
| 508 | + |
| 509 | + m_Model->GetRenderer()->SetCameraState(cam); |
| 510 | + |
| 511 | + m_Model->GetParentUI()->GetSystemInterface()->GetHistoryManager()->UpdateHistory( |
| 512 | + "CameraViewpoint", file.toStdString(), true); |
| 513 | + } |
| 514 | + catch (QString &error_str) |
| 515 | + { |
| 516 | + QMessageBox::warning(this, tr("Import camera viewpoint error"), error_str); |
| 517 | + } |
| 518 | +} |
| 519 | + |
| 520 | +void ViewPanel3D::SaveCameraViewpoint(QString file) |
| 521 | +{ |
| 522 | + CameraState cam = m_Model->GetRenderer()->GetCameraState(); |
| 523 | + QJsonObject json_main; |
| 524 | + QJsonObject json_camera; |
| 525 | + |
| 526 | + // Vector3d to QJsonArray |
| 527 | + QJsonArray camPosition; |
| 528 | + for (const double& x : cam.position) |
| 529 | + camPosition += x; |
| 530 | + |
| 531 | + // Vector3d to QJsonArray |
| 532 | + QJsonArray camFocalPoint; |
| 533 | + for (const double& x : cam.focal_point) |
| 534 | + camFocalPoint += x; |
| 535 | + |
| 536 | + // Vector3d to QJsonArray |
| 537 | + QJsonArray camViewUp; |
| 538 | + for (const double& x : cam.view_up) |
| 539 | + camViewUp += x; |
| 540 | + |
| 541 | + // Vector2d to QJsonArray |
| 542 | + QJsonArray camClippingRange; |
| 543 | + for (const double& x : cam.clipping_range) |
| 544 | + camClippingRange += x; |
| 545 | + |
| 546 | + // Fields from vtkCamera |
| 547 | + json_camera["position"] = camPosition; |
| 548 | + json_camera["focal_point"] = camFocalPoint; |
| 549 | + json_camera["view_up"] = camViewUp; |
| 550 | + json_camera["clipping_range"] = camClippingRange; |
| 551 | + json_camera["view_angle"] = QJsonValue(cam.view_angle); |
| 552 | + json_camera["parallel_scale"] = QJsonValue(cam.parallel_scale); |
| 553 | + json_camera["parallel_projection"] = QJsonValue(cam.parallel_projection).toBool(); |
| 554 | + |
| 555 | + // Main json with header and version |
| 556 | + json_main["file_type"] = QJsonValue("itksnap_camera_viewpoint"); |
| 557 | + json_main["version"] = QJsonValue("1.0.0"); |
| 558 | + json_main["camera"] = json_camera; |
| 559 | + |
| 560 | + QFile saveFile(file); |
| 561 | + if (!saveFile.open(QIODevice::WriteOnly) |
| 562 | + || saveFile.write(QJsonDocument(json_main).toJson()) == -1) |
| 563 | + QMessageBox::warning(this, tr("Export camera viewpoint error"), |
| 564 | + tr("I/O error: %1").arg(saveFile.errorString())); |
| 565 | + |
| 566 | + m_Model->GetParentUI()->GetSystemInterface()->GetHistoryManager()->UpdateHistory( |
| 567 | + "CameraViewpoint", file.toStdString(), true); |
| 568 | +} |
| 569 | + |
| 570 | +void ViewPanel3D::on_actionImport_Viewpoint_triggered() |
| 571 | +{ |
| 572 | + // Ask for a filename |
| 573 | + QString selection = ShowSimpleOpenDialogWithHistory(this, |
| 574 | + m_Model->GetParentUI(), |
| 575 | + "CameraViewpoint", |
| 576 | + tr("Load Camera Viewpoint - ITK-SNAP"), |
| 577 | + tr("Camera Viewpoint JSON File"), |
| 578 | + tr("Camera Files (%1)").arg("*.json")); |
| 579 | + |
| 580 | + // Open the labels from the selection |
| 581 | + if(selection.length()) |
| 582 | + LoadCameraViewpoint(selection); |
| 583 | +} |
| 584 | + |
| 585 | +void ViewPanel3D::on_actionExport_Viewpoint_triggered() |
| 586 | +{ |
| 587 | + // Ask for a filename |
| 588 | + QString selection = ShowSimpleSaveDialogWithHistory( |
| 589 | + this, m_Model->GetParentUI(), "CameraViewpoint", |
| 590 | + tr("Save Camera Viewpoint - ITK-SNAP"), |
| 591 | + tr("Camera Viewpoint JSON File"), |
| 592 | + tr("Camera Files (%1)").arg("*.json"), |
| 593 | + true); |
| 594 | + |
| 595 | + // Open the labels from the selection |
| 596 | + if(selection.length()) |
| 597 | + SaveCameraViewpoint(selection); |
| 598 | +} |
| 599 | + |
411 | 600 | void ViewPanel3D::on_actionContinuous_Update_triggered() |
412 | 601 | { |
413 | 602 | ui->btnUpdateMesh->setVisible(!ui->actionContinuous_Update->isChecked()); |
|
0 commit comments