Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/polyscope/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ struct Context {
glm::mat4x4 viewMat;
double fov = view::defaultFov;
ProjectionMode projectionMode = ProjectionMode::Perspective;
glm::vec3 viewCenter;
bool midflight = false;
float flightStartTime = -1;
float flightEndTime = -1;
Expand Down
11 changes: 10 additions & 1 deletion include/polyscope/view.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ extern std::array<float, 4>& bgColor;
extern glm::mat4x4& viewMat;
extern double& fov; // in the y direction
extern ProjectionMode& projectionMode;
extern glm::vec3& viewCenter; // center about which view transformations are performed

// "Flying" view members
extern bool& midflight;
Expand Down Expand Up @@ -99,6 +100,13 @@ void lookAt(glm::vec3 cameraLocation, glm::vec3 target, glm::vec3 upDir, bool fl
glm::mat4 computeHomeView();
void resetCameraToHomeView();
void flyToHomeView();
void setViewCenter(glm::vec3 newCenter, bool flyTo = false);

// These both set the new value, and project the current view as-needed to conform to the new setting
void updateViewAndChangeNavigationStyle(NavigateStyle newStyle, bool flyTo = false);
void updateViewAndChangeUpDir(UpDir newUpDir, bool flyTo = false);
void updateViewAndChangeFrontDir(FrontDir newFrontDir, bool flyTo = false);
void updateViewAndChangeCenter(glm::vec3 newCenter, bool flyTo = false);

// Move the camera with a 'flight' where the camera's position is briefly animated
void startFlightTo(const CameraParameters& p, float flightLengthInSeconds = .4);
Expand Down Expand Up @@ -175,8 +183,9 @@ void ensureViewValid();
void processTranslate(glm::vec2 delta);
void processRotate(glm::vec2 startP, glm::vec2 endP);
void processClipPlaneShift(double amount);
void processZoom(double amount);
void processZoom(double amount, bool relativeToCenter = false);
void processKeyboardNavigation(ImGuiIO& io);
void processSetCenter(glm::vec2 screenCoords);

// deprecated, bad names, see variants above
glm::vec3 bufferCoordsToWorldRay(glm::vec2 bufferCoords);
Expand Down
161 changes: 93 additions & 68 deletions src/polyscope.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,9 @@ float dragDistSinceLastRelease = 0.0;
void processInputEvents() {
ImGuiIO& io = ImGui::GetIO();

// RECALL: in ImGUI language, on MacOS "ctrl" == "cmd", so all the options
// below referring to ctrl really mean cmd on MacOS.

// If any mouse button is pressed, trigger a redraw
if (ImGui::IsAnyMouseDown()) {
requestRedraw();
Expand All @@ -358,90 +361,108 @@ void processInputEvents() {
}
}

// === Mouse inputs
if (!io.WantCaptureMouse && !widgetCapturedMouse) {
double xoffset = io.MouseWheelH;
double yoffset = io.MouseWheel;

if (xoffset != 0 || yoffset != 0) {
requestRedraw();
{ // Process scroll via "mouse wheel" (which might be a touchpad)
double xoffset = io.MouseWheelH;
double yoffset = io.MouseWheel;

// On some setups, shift flips the scroll direction, so take the max
// scrolling in any direction
double maxScroll = xoffset;
if (std::abs(yoffset) > std::abs(xoffset)) {
maxScroll = yoffset;
}
if (xoffset != 0 || yoffset != 0) {
requestRedraw();

// Pass camera commands to the camera
if (maxScroll != 0.0) {
bool scrollClipPlane = io.KeyShift;
// On some setups, shift flips the scroll direction, so take the max
// scrolling in any direction
double maxScroll = xoffset;
if (std::abs(yoffset) > std::abs(xoffset)) {
maxScroll = yoffset;
}

// Pass camera commands to the camera
if (maxScroll != 0.0) {
bool scrollClipPlane = io.KeyShift && !io.KeyCtrl;
bool relativeZoom = io.KeyShift && io.KeyCtrl;

if (scrollClipPlane) {
view::processClipPlaneShift(maxScroll);
} else {
view::processZoom(maxScroll);
if (scrollClipPlane) {
view::processClipPlaneShift(maxScroll);
} else {
view::processZoom(maxScroll, relativeZoom);
}
}
}
}
}

// === Mouse inputs
if (!io.WantCaptureMouse && !widgetCapturedMouse) {

// Process drags
bool dragLeft = ImGui::IsMouseDragging(0);
bool dragRight = !dragLeft && ImGui::IsMouseDragging(1); // left takes priority, so only one can be true
if (dragLeft || dragRight) {
{ // Process drags
bool dragLeft = ImGui::IsMouseDragging(0);
bool dragRight = !dragLeft && ImGui::IsMouseDragging(1); // left takes priority, so only one can be true
if (dragLeft || dragRight) {

glm::vec2 dragDelta{io.MouseDelta.x / view::windowWidth, -io.MouseDelta.y / view::windowHeight};
dragDistSinceLastRelease += std::abs(dragDelta.x);
dragDistSinceLastRelease += std::abs(dragDelta.y);
glm::vec2 dragDelta{io.MouseDelta.x / view::windowWidth, -io.MouseDelta.y / view::windowHeight};
dragDistSinceLastRelease += std::abs(dragDelta.x);
dragDistSinceLastRelease += std::abs(dragDelta.y);

// exactly one of these will be true
bool isRotate = dragLeft && !io.KeyShift && !io.KeyCtrl;
bool isTranslate = (dragLeft && io.KeyShift && !io.KeyCtrl) || dragRight;
bool isDragZoom = dragLeft && io.KeyShift && io.KeyCtrl;
// exactly one of these will be true
bool isRotate = dragLeft && !io.KeyShift && !io.KeyCtrl;
bool isTranslate = (dragLeft && io.KeyShift && !io.KeyCtrl) || dragRight;
bool isDragZoom = dragLeft && io.KeyShift && io.KeyCtrl;

if (isDragZoom) {
view::processZoom(dragDelta.y * 5);
}
if (isRotate) {
glm::vec2 currPos{io.MousePos.x / view::windowWidth,
(view::windowHeight - io.MousePos.y) / view::windowHeight};
currPos = (currPos * 2.0f) - glm::vec2{1.0, 1.0};
if (std::abs(currPos.x) <= 1.0 && std::abs(currPos.y) <= 1.0) {
view::processRotate(currPos - 2.0f * dragDelta, currPos);
if (isDragZoom) {
view::processZoom(dragDelta.y * 5, true);
}
if (isRotate) {
glm::vec2 currPos{io.MousePos.x / view::windowWidth,
(view::windowHeight - io.MousePos.y) / view::windowHeight};
currPos = (currPos * 2.0f) - glm::vec2{1.0, 1.0};
if (std::abs(currPos.x) <= 1.0 && std::abs(currPos.y) <= 1.0) {
view::processRotate(currPos - 2.0f * dragDelta, currPos);
}
}
if (isTranslate) {
view::processTranslate(dragDelta);
}
}
if (isTranslate) {
view::processTranslate(dragDelta);
}
}

// Click picks
float dragIgnoreThreshold = 0.01;
if (ImGui::IsMouseReleased(0)) {
{ // Click picks
float dragIgnoreThreshold = 0.01;
bool anyModifierHeld = io.KeyShift || io.KeyCtrl || io.KeyAlt;
bool ctrlShiftHeld = io.KeyShift && io.KeyCtrl;

if (!anyModifierHeld && io.MouseReleased[0]) {

// Don't pick at the end of a long drag
if (dragDistSinceLastRelease < dragIgnoreThreshold) {
ImVec2 p = ImGui::GetMousePos();
PickResult pickResult = pickAtScreenCoords(glm::vec2{p.x, p.y});
setSelection(pickResult);
// Don't pick at the end of a long drag
if (dragDistSinceLastRelease < dragIgnoreThreshold) {
glm::vec2 screenCoords{io.MousePos.x, io.MousePos.y};
PickResult pickResult = pickAtScreenCoords(screenCoords);
setSelection(pickResult);
}
}

// Reset the drag distance after any release
dragDistSinceLastRelease = 0.0;
}
// Clear pick
if (ImGui::IsMouseReleased(1)) {
if (dragDistSinceLastRelease < dragIgnoreThreshold) {
resetSelection();
// Clear pick
if (!anyModifierHeld && io.MouseReleased[1]) {
if (dragDistSinceLastRelease < dragIgnoreThreshold) {
resetSelection();
}
dragDistSinceLastRelease = 0.0;
}

// Ctrl-shift left-click to set new center
if (ctrlShiftHeld && io.MouseReleased[0]) {
if (dragDistSinceLastRelease < dragIgnoreThreshold) {
glm::vec2 screenCoords{io.MousePos.x, io.MousePos.y};
view::processSetCenter(screenCoords);
}
}
dragDistSinceLastRelease = 0.0;
}
}
}

// Reset the drag distance after any release
if (io.MouseReleased[0]) {
dragDistSinceLastRelease = 0.0;
}

// === Key-press inputs
if (!io.WantCaptureKeyboard) {
view::processKeyboardNavigation(io);
Expand Down Expand Up @@ -634,15 +655,18 @@ void buildPolyscopeGui() {

// clang-format off
ImGui::Begin("Controls", NULL, ImGuiWindowFlags_NoTitleBar);
ImGui::TextUnformatted("View Navigation:");
ImGui::TextUnformatted(" Rotate: [left click drag]");
ImGui::TextUnformatted(" Translate: [shift] + [left click drag] OR [right click drag]");
ImGui::TextUnformatted(" Zoom: [scroll] OR [ctrl] + [shift] + [left click drag]");
ImGui::TextUnformatted(" Use [ctrl-c] and [ctrl-v] to save and restore camera poses");
ImGui::TextUnformatted("View Navigation:");
ImGui::TextUnformatted(" Rotate: [left click drag]");
ImGui::TextUnformatted(" Translate: [shift] + [left click drag] OR [right click drag]");
ImGui::TextUnformatted(" Zoom: [scroll] OR [ctrl/cmd] + [shift] + [left click drag]");
ImGui::TextUnformatted(" Use [ctrl/cmd-c] and [ctrl/cmd-v] to save and restore camera poses");
ImGui::TextUnformatted(" via the clipboard.");
ImGui::TextUnformatted("\nMenu Navigation:");
ImGui::TextUnformatted(" Hold [ctrl/cmd] + [shift] and [left click] in the scene to set the");
ImGui::TextUnformatted(" orbit center.");
ImGui::TextUnformatted(" Hold [ctrl/cmd] + [shift] and scroll to zoom towards the center.");
ImGui::TextUnformatted("\nMenu Navigation:");
ImGui::TextUnformatted(" Menu headers with a '>' can be clicked to collapse and expand.");
ImGui::TextUnformatted(" Use [ctrl] + [left click] to manually enter any numeric value");
ImGui::TextUnformatted(" Use [ctrl/cmd] + [left click] to manually enter any numeric value");
ImGui::TextUnformatted(" via the keyboard.");
ImGui::TextUnformatted(" Press [space] to dismiss popup dialogs.");
ImGui::TextUnformatted("\nSelection:");
Expand Down Expand Up @@ -1095,8 +1119,9 @@ bool hasStructure(std::string type, std::string name) {
// Special automatic case, return any
if (name == "") {
if (sMap.size() != 1) {
exception("Cannot use automatic has-structure test with empty name unless there is exactly one structure of that type "
"registered");
exception(
"Cannot use automatic has-structure test with empty name unless there is exactly one structure of that type "
"registered");
}
return true;
}
Expand Down
Loading
Loading