Skip to content

Commit c39adac

Browse files
committed
Merge branch 'python-kin-refactor' of github.com:LonghornRacingElectric/simulation_toolkit into python-kin-refactor
2 parents 9963174 + ab770cd commit c39adac

File tree

7 files changed

+286
-55
lines changed

7 files changed

+286
-55
lines changed

src/applications/opengl/imgui.ini

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,24 @@ Pos=60,60
33
Size=400,400
44

55
[Window][Cube Control]
6-
Pos=60,60
6+
Pos=49,59
77
Size=362,142
88

99
[Window][Node Info]
10-
Pos=86,86
10+
Pos=73,90
1111
Size=418,505
1212
Collapsed=1
1313

1414
[Window][Rod Info]
15-
Pos=74,49
16-
Size=452,289
15+
Pos=73,115
16+
Size=756,840
1717
Collapsed=1
1818

1919
[Window][Control Panel]
20-
Pos=419,581
21-
Size=589,213
20+
Pos=1380,28
21+
Size=589,270
22+
23+
[Window][Model Rotation]
24+
Pos=0,1045
25+
Size=1908,50
2226

src/applications/opengl/main

17.9 KB
Binary file not shown.

src/applications/opengl/model.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
nodes:
22
# FL (Front Left) QuarterCar nodes
3-
- position: [0,0,0]
3+
- position: [-0.73,0,0]
44
- position: [0.087376, 0.215900, 0.090000] # FL lower wishbone fore inboard
55
- position: [-0.095250, 0.215900, 0.090000] # FL lower wishbone aft inboard
66
- position: [0, 0.556499, 0.124998] # FL lower wishbone outboard

src/applications/opengl/src/imgui.h

Lines changed: 187 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "../glm/gtc/matrix_transform.hpp"
99
#include "../glm/gtc/type_ptr.hpp"
1010
#include <vector>
11+
#include <float.h>
1112
#include "GLFW/glfw3.h" // For GLFWwindow
1213
#include "sphere.h"
1314

@@ -30,7 +31,9 @@ struct RodInstance {
3031
extern int x, y, z;
3132
extern bool createNewNode;
3233
extern bool createNewRod;
33-
extern float camAngleX, camAngleY, camAngleZ, camDistance, zoom;
34+
extern float camAngleX, camAngleZ, camDistance, zoom;
35+
extern int selectedNodeIndex, selectedRodIndex;
36+
extern glm::vec3 recenterOffset;
3437
extern float xyzRodEnd1[3];
3538
extern float xyzRodEnd2[3];
3639
extern std::vector<SphereInstance> spheres;
@@ -45,6 +48,120 @@ void initImGui(GLFWwindow* window) {
4548
ImGui_ImplOpenGL3_Init("#version 330");
4649
}
4750

51+
// --------- Keyboard Input Handling --------- //
52+
// Handle object selection with Shift+Click
53+
void handleObjectSelection(GLFWwindow* window) {
54+
// Only handle Shift+Click for object selection
55+
bool shiftPressed = glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS ||
56+
glfwGetKey(window, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS;
57+
58+
if (shiftPressed && glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS) {
59+
static bool wasPressed = false;
60+
if (!wasPressed) { // Only trigger on press, not hold
61+
wasPressed = true;
62+
63+
double xpos, ypos;
64+
glfwGetCursorPos(window, &xpos, &ypos);
65+
66+
int width, height;
67+
glfwGetWindowSize(window, &width, &height);
68+
69+
// Convert screen coordinates to normalized device coordinates
70+
float x = (2.0f * xpos) / width - 1.0f;
71+
float y = 1.0f - (2.0f * ypos) / height;
72+
73+
// Simple selection logic - find closest object to mouse position
74+
float minDistance = 0.3f; // Selection threshold
75+
int closestNode = -1;
76+
int closestRod = -1;
77+
float closestNodeDist = FLT_MAX; // Start with very large distance
78+
float closestRodDist = FLT_MAX; // Start with very large distance
79+
80+
// Check nodes (spheres) - use actual world positions
81+
for (int i = 0; i < spheres.size(); i++) {
82+
glm::vec3 worldPos = spheres[i].position;
83+
// Convert to screen space (very simplified - assumes orthographic projection)
84+
float screenX = worldPos.x * 0.3f; // Scale factor for ortho projection
85+
float screenY = worldPos.y * 0.3f;
86+
87+
float distance = sqrt((x - screenX) * (x - screenX) + (y - screenY) * (y - screenY));
88+
if (distance < closestNodeDist) {
89+
closestNode = i;
90+
closestNodeDist = distance;
91+
}
92+
}
93+
94+
// Check rods
95+
for (int i = 0; i < rods.size(); i++) {
96+
glm::vec3 mid = (rods[i].end1 + rods[i].end2) * 0.5f;
97+
float screenX = mid.x * 0.3f;
98+
float screenY = mid.y * 0.3f;
99+
100+
float distance = sqrt((x - screenX) * (x - screenX) + (y - screenY) * (y - screenY));
101+
if (distance < closestRodDist) {
102+
closestRod = i;
103+
closestRodDist = distance;
104+
}
105+
}
106+
107+
// Always clear selections first
108+
selectedNodeIndex = -1;
109+
selectedRodIndex = -1;
110+
111+
// Select the closest object (node or rod) if it's within threshold
112+
if (closestNodeDist < closestRodDist && closestNodeDist < minDistance) {
113+
selectedNodeIndex = closestNode;
114+
} else if (closestRodDist < minDistance) {
115+
selectedRodIndex = closestRod;
116+
}
117+
// If no object is close enough, selections remain cleared (deselection)
118+
}
119+
} else {
120+
static bool wasPressed = false;
121+
wasPressed = false; // Reset when button is released or shift is not pressed
122+
}
123+
}
124+
125+
void handleKeyboardInput(GLFWwindow* window) {
126+
// Arrow key controls for camera
127+
float step = 2.0f; // Step size for camera angle changes
128+
float zoomStep = 0.1f; // Step size for zoom changes
129+
130+
// Check if Shift is pressed
131+
bool shiftPressed = (glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS ||
132+
glfwGetKey(window, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS);
133+
134+
if (shiftPressed) {
135+
// Shift + arrow keys for Z axis and zoom controls
136+
if (glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS) {
137+
camAngleZ -= step;
138+
if (camAngleZ < -180.0f) camAngleZ += 360.0f;
139+
}
140+
if (glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS) {
141+
camAngleZ += step;
142+
if (camAngleZ > 180.0f) camAngleZ -= 360.0f;
143+
}
144+
if (glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS) {
145+
zoom += zoomStep;
146+
if (zoom > 3.0f) zoom = 3.0f;
147+
}
148+
if (glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS) {
149+
zoom -= zoomStep;
150+
if (zoom < 0.1f) zoom = 0.1f;
151+
}
152+
} else {
153+
// Regular arrow keys for X axis controls only (limited to -89 to 89 degrees)
154+
if (glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS) {
155+
camAngleX += step;
156+
if (camAngleX > 89.0f) camAngleX = 89.0f;
157+
}
158+
if (glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS) {
159+
camAngleX -= step;
160+
if (camAngleX < -89.0f) camAngleX = -89.0f;
161+
}
162+
}
163+
}
164+
48165
// --------- ImGui UI Controls --------- //
49166
void renderImGuiControls() {
50167
ImGui_ImplOpenGL3_NewFrame();
@@ -65,9 +182,16 @@ void renderImGuiControls() {
65182
ImGui::Separator();
66183
ImGui::Text("Camera Controls");
67184
ImGui::SliderFloat("Orbit X (up/down)", &camAngleX, -89.0f, 89.0f);
68-
ImGui::SliderFloat("Orbit Y (left/right)", &camAngleY, 0.0f, 360.0f);
69-
ImGui::SliderFloat("Orbit Z (roll)", &camAngleZ, -180.0f, 180.0f); // Add this line
185+
ImGui::SliderFloat("Orbit Z (roll)", &camAngleZ, 0.0f, 360.0f);
70186
ImGui::SliderFloat("Zoom (distance)", &zoom, 0.1f, 3.0f);
187+
188+
ImGui::Separator();
189+
ImGui::Text("Recenter Controls");
190+
ImGui::DragFloat3("Recenter Offset", glm::value_ptr(recenterOffset), 0.01f);
191+
ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.0f, 1.0f), "Note: Recenter offset is applied when loading YAML");
192+
193+
ImGui::Separator();
194+
ImGui::TextColored(ImVec4(0.0f, 0.0f, 1.0f, 1.0f), "Click on items in dropdowns to select");
71195

72196
ImGui::End();
73197

@@ -76,23 +200,46 @@ void renderImGuiControls() {
76200

77201
if (ImGui::Button("Clear All Nodes")) {
78202
spheres.clear();
203+
selectedNodeIndex = -1; // Clear selection when clearing all nodes
79204
}
80205

81206
for (int i = 0; i < static_cast<int>(spheres.size()); ++i) {
82207
std::string header = "Node " + std::to_string(i);
83-
if (ImGui::CollapsingHeader(header.c_str())) {
84-
ImGui::PushID(i);
85-
208+
bool isSelected = (i == selectedNodeIndex);
209+
210+
// Highlight selected node with blue background
211+
if (isSelected) {
212+
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.0f, 0.0f, 1.0f, 0.3f)); // Blue background
213+
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.0f, 0.0f, 1.0f, 0.5f)); // Brighter blue on hover
214+
}
215+
216+
// Make the header clickable for selection
217+
if (ImGui::Selectable(header.c_str(), isSelected, ImGuiSelectableFlags_AllowItemOverlap)) {
218+
// Clear other selections and select this node
219+
selectedNodeIndex = i;
220+
selectedRodIndex = -1;
221+
}
222+
223+
// Show properties if this node is selected
224+
if (isSelected) {
225+
ImGui::Indent();
86226
ImGui::DragFloat3("Position", glm::value_ptr(spheres[i].position), 0.1f);
87227
ImGui::ColorEdit4("Color", glm::value_ptr(spheres[i].color));
88-
228+
89229
if (ImGui::Button("Delete Node")) {
90230
spheres.erase(spheres.begin() + i);
91-
ImGui::PopID();
231+
selectedNodeIndex = -1; // Clear selection if deleting selected node
232+
ImGui::Unindent();
233+
// Pop the blue background colors before breaking
234+
ImGui::PopStyleColor(2);
92235
break;
93236
}
94-
95-
ImGui::PopID();
237+
ImGui::Unindent();
238+
}
239+
240+
// Pop the blue background colors if this was the selected node
241+
if (isSelected) {
242+
ImGui::PopStyleColor(2);
96243
}
97244
}
98245

@@ -103,24 +250,47 @@ void renderImGuiControls() {
103250

104251
if (ImGui::Button("Clear All Rods")) {
105252
rods.clear();
253+
selectedRodIndex = -1; // Clear selection when clearing all rods
106254
}
107255

108256
for (int i = 0; i < static_cast<int>(rods.size()); ++i) {
109257
std::string header = "Rod " + std::to_string(i);
110-
if (ImGui::CollapsingHeader(header.c_str())) {
111-
ImGui::PushID(i);
112-
258+
bool isSelected = (i == selectedRodIndex);
259+
260+
// Highlight selected rod with blue background
261+
if (isSelected) {
262+
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.0f, 0.0f, 1.0f, 0.3f)); // Blue background
263+
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.0f, 0.0f, 1.0f, 0.5f)); // Brighter blue on hover
264+
}
265+
266+
// Make the header clickable for selection
267+
if (ImGui::Selectable(header.c_str(), isSelected, ImGuiSelectableFlags_AllowItemOverlap)) {
268+
// Clear other selections and select this rod
269+
selectedRodIndex = i;
270+
selectedNodeIndex = -1;
271+
}
272+
273+
// Show properties if this rod is selected
274+
if (isSelected) {
275+
ImGui::Indent();
113276
ImGui::DragFloat3("Start", glm::value_ptr(rods[i].end1), 0.1f);
114277
ImGui::DragFloat3("End", glm::value_ptr(rods[i].end2), 0.1f);
115278
ImGui::ColorEdit4("Color", glm::value_ptr(rods[i].color));
116-
279+
117280
if (ImGui::Button("Delete Rod")) {
118281
rods.erase(rods.begin() + i);
119-
ImGui::PopID();
282+
selectedRodIndex = -1; // Clear selection if deleting selected rod
283+
ImGui::Unindent();
284+
// Pop the blue background colors before breaking
285+
ImGui::PopStyleColor(2);
120286
break;
121287
}
122-
123-
ImGui::PopID();
288+
ImGui::Unindent();
289+
}
290+
291+
// Pop the blue background colors if this was the selected rod
292+
if (isSelected) {
293+
ImGui::PopStyleColor(2);
124294
}
125295
}
126296

src/applications/opengl/src/info.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@ Commands:
88
imgui/imgui_widgets.cpp \
99
imgui/imgui_impl_glfw.cpp \
1010
imgui/imgui_impl_opengl3.cpp \
11-
-lglfw -ldl -lGL -I./imgui -I./src
11+
-lglfw -ldl -lGL -I./imgui -I./src
1212

1313
2. ./main
1414

15+
3. (optional) if segmentation fault, before ./main:
16+
export MESA_LOADER_DRIVER_OVERRIDE=zink
17+
1518

1619
temp.cpp: Cube Model (just for demo)
1720
Commands:

0 commit comments

Comments
 (0)