diff options
Diffstat (limited to 'Project/Workspace.cpp')
-rw-r--r-- | Project/Workspace.cpp | 539 |
1 files changed, 539 insertions, 0 deletions
diff --git a/Project/Workspace.cpp b/Project/Workspace.cpp new file mode 100644 index 0000000..26c913b --- /dev/null +++ b/Project/Workspace.cpp @@ -0,0 +1,539 @@ +#include "Workspace.h" + +#include "Element.h" +#include "Bus.h" +#include "Line.h" + +// Camera +Camera::Camera() +{ + m_translation = wxPoint2DDouble(0, 0); + m_scale = 1.0; +} + +Camera::~Camera() {} +wxPoint2DDouble Camera::ScreenToWorld(wxPoint2DDouble screenCoords) const +{ + return wxPoint2DDouble(screenCoords.m_x / m_scale - m_translation.m_x, + screenCoords.m_y / m_scale - m_translation.m_y); +} + +void Camera::SetTranslation(wxPoint2DDouble screenPoint) +{ + m_translation = screenPoint / m_scale - m_translationStartPt; +} + +void Camera::SetScale(wxPoint2DDouble screenPoint, double delta) +{ + m_translation -= screenPoint * (1.0 - m_scale) / m_scale; + + m_scale += delta; + + // Limits: 5% - 300% + if(m_scale < 0.05) m_scale = 0.05; + if(m_scale > 3.0) m_scale = 3.0; + + m_translation += screenPoint * (1.0 - m_scale) / m_scale; +} + +wxPoint2DDouble Camera::GetMousePosition(bool worldCoords) const +{ + if(worldCoords) return ScreenToWorld(m_mousePosition); + return m_mousePosition; +} + +// Workspace +Workspace::Workspace() : WorkspaceBase(NULL) {} +Workspace::Workspace(wxWindow* parent, wxString name, wxStatusBar* statusBar) : WorkspaceBase(parent) +{ + m_name = name; + m_statusBar = statusBar; + m_glContext = new wxGLContext(m_glCanvas); + m_camera = new Camera(); + m_selectionRect = wxRect2DDouble(0, 0, 0, 0); + + const int widths[4] = {-3, -1, 100, 100}; + m_statusBar->SetStatusWidths(4, widths); +} + +Workspace::~Workspace() +{ + for(auto it = m_elementList.begin(); it != m_elementList.end(); ++it) { + if(*it) delete *it; + } + m_elementList.clear(); + if(m_camera) delete m_camera; +} + +void Workspace::OnPaint(wxPaintEvent& event) +{ + wxPaintDC dc(m_glCanvas); + m_glContext->SetCurrent(*m_glCanvas); + SetViewport(); + + // Set GLCanvas scale and translation. + glScaled(m_camera->GetScale(), m_camera->GetScale(), 0.0); // Scale + glTranslated(m_camera->GetTranslation().m_x, m_camera->GetTranslation().m_y, 0.0); // Translation + + // Draw + + // Elements + for(auto it = m_elementList.begin(); it != m_elementList.end(); ++it) { + Element* element = *it; + element->Draw(m_camera->GetTranslation(), m_camera->GetScale()); + } + + // Selection rectangle + glColor4d(0.0, 0.5, 1.0, 1.0); + glBegin(GL_LINE_LOOP); + glVertex2d(m_selectionRect.m_x, m_selectionRect.m_y); + glVertex2d(m_selectionRect.m_x, m_selectionRect.m_y + m_selectionRect.m_height); + glVertex2d(m_selectionRect.m_x + m_selectionRect.m_width, m_selectionRect.m_y + m_selectionRect.m_height); + glVertex2d(m_selectionRect.m_x + m_selectionRect.m_width, m_selectionRect.m_y); + glEnd(); + glColor4d(0.0, 0.5, 1.0, 0.3); + glBegin(GL_QUADS); + glVertex2d(m_selectionRect.m_x, m_selectionRect.m_y); + glVertex2d(m_selectionRect.m_x, m_selectionRect.m_y + m_selectionRect.m_height); + glVertex2d(m_selectionRect.m_x + m_selectionRect.m_width, m_selectionRect.m_y + m_selectionRect.m_height); + glVertex2d(m_selectionRect.m_x + m_selectionRect.m_width, m_selectionRect.m_y); + glEnd(); + + glFlush(); // Sends all pending information directly to the GPU. + m_glCanvas->SwapBuffers(); + event.Skip(); +} + +void Workspace::SetViewport() +{ + glClearColor(1.0, 1.0, 1.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + glDisable(GL_DEPTH_TEST); + glEnable(GL_TEXTURE_2D); + glEnable(GL_COLOR_MATERIAL); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_LINE_SMOOTH); + + double width = m_glCanvas->GetSize().x - 1; + double height = m_glCanvas->GetSize().y - 1; + + // Viewport fit the screen. + glViewport(0, 0, width, height); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluOrtho2D(0.0, width, height, 0.0); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +} + +void Workspace::OnLeftClickDown(wxMouseEvent& event) +{ + bool foundElement = false; + if(m_mode == MODE_INSERT) { + // Get the last element inserted on the list. + Element* newElement = *(m_elementList.end() - 1); + for(auto it = m_elementList.begin(); it != m_elementList.end(); ++it) { + Element* element = *it; + // Clicked in any element. + if(element->Contains(m_camera->ScreenToWorld(event.GetPosition()))) { + // Click at a bus. + if(typeid(*element) == typeid(Bus)) { + // Select the bus. + element->SetSelected(); + foundElement = true; // Element found. + // Add the new element's parent. If the element being inserted return true, return + // to edit mode. + if(newElement->AddParent(element, m_camera->ScreenToWorld(event.GetPosition()))) { + m_mode = MODE_EDIT; + } + } + } + } + // The line element can have an indefined number of points. + if(!foundElement) { + if(typeid(*newElement) == typeid(Line)) { + newElement->AddPoint(m_camera->ScreenToWorld(event.GetPosition())); + } + } + foundElement = true; + } + else + { + bool clickPickbox = false; + for(auto it = m_elementList.begin(); it != m_elementList.end(); ++it) { + Element* element = *it; + element->ResetPickboxes(); // Reset pickbox state. + element->StartMove(m_camera->ScreenToWorld( + event.GetPosition())); // Set movement initial position (not necessarily will be moved). + + // Click at an element + if(element->Contains(m_camera->ScreenToWorld(event.GetPosition()))) { + if(!foundElement) { + // Select and show pickbox. + element->SetSelected(); + element->ShowPickbox(); + foundElement = true; + } + // If pickbox contains the click, move the pickbox + if(element->PickboxContains(m_camera->ScreenToWorld(event.GetPosition()))) { + m_mode = MODE_MOVE_PICKBOX; + clickPickbox = true; + } + // If didn't foud a pickbox, move the element + if(!clickPickbox) { + m_mode = MODE_MOVE_ELEMENT; + } + } + } + } + + if(!foundElement) { + m_mode = MODE_SELECTION_RECT; + m_startSelRect = m_camera->ScreenToWorld(event.GetPosition()); + } + + Redraw(); + UpdateStatusBar(); + event.Skip(); +} + +void Workspace::OnRightClickDown(wxMouseEvent& event) +{ + if(m_mode == MODE_EDIT) { + for(auto it = m_elementList.begin(); it != m_elementList.end(); ++it) { + Element* element = *it; + if(element->IsSelected()) { + if(element->Contains(m_camera->ScreenToWorld(event.GetPosition()))) { + wxMenu menu; + if(element->GetContextMenu(menu)) { + menu.SetClientData(element); + menu.Connect(wxEVT_COMMAND_MENU_SELECTED, + wxCommandEventHandler(Workspace::OnPopupClick), NULL, this); + PopupMenu(&menu); + } + } + } + } + } +} + +void Workspace::OnLeftClickUp(wxMouseEvent& event) +{ + bool foundPickbox = false; + for(auto it = m_elementList.begin(); it != m_elementList.end(); ++it) { + Element* element = *it; + + if(m_mode == MODE_SELECTION_RECT) { + if(element->Intersects(m_selectionRect)) { + element->SetSelected(); + } + else + { + element->SetSelected(false); + } + } + else + { + // Deselect + if(!event.ControlDown()) { + if(!element->Contains(m_camera->ScreenToWorld(event.GetPosition()))) { + element->SetSelected(false); + } + } + + if(element->PickboxContains(m_camera->ScreenToWorld(event.GetPosition()))) { + foundPickbox = true; + } + else + { + element->ShowPickbox(false); + } + } + } + if(!foundPickbox) { + SetCursor(wxCURSOR_ARROW); + } + + if(m_mode != MODE_INSERT) { + m_mode = MODE_EDIT; + } + m_selectionRect = wxRect2DDouble(0, 0, 0, 0); + Redraw(); + UpdateStatusBar(); +} + +void Workspace::OnMouseMotion(wxMouseEvent& event) +{ + switch(m_mode) + { + case MODE_INSERT: + { + Element* newElement = *(m_elementList.end() - 1); + newElement->SetPosition(m_camera->ScreenToWorld(event.GetPosition())); + Redraw(); + } + break; + + case MODE_DRAG: + { + m_camera->SetTranslation(event.GetPosition()); + Redraw(); + } + break; + + case MODE_EDIT: + { + bool foundPickbox = false; + bool redraw = false; + for(auto it = m_elementList.begin(); it != m_elementList.end(); ++it) { + Element* element = *it; + if(element->IsSelected()) { + if(element->Contains(m_camera->ScreenToWorld(event.GetPosition()))) { + element->ShowPickbox(); + redraw = true; + + if(element->PickboxContains(m_camera->ScreenToWorld(event.GetPosition()))) { + foundPickbox = true; + SetCursor(element->GetBestPickboxCursor()); + } + else if(!foundPickbox) + { + SetCursor(wxCURSOR_ARROW); + } + } + else if(!foundPickbox) + { + if(element->IsPickboxShown()) redraw = true; + + element->ShowPickbox(false); + SetCursor(wxCURSOR_ARROW); + } + } + } + if(redraw) Redraw(); + } + break; + + case MODE_MOVE_PICKBOX: + { + for(auto it = m_elementList.begin(); it != m_elementList.end(); ++it) { + Element* element = *it; + if(element->IsSelected()) { + element->MovePickbox(m_camera->ScreenToWorld(event.GetPosition())); + Redraw(); + } + } + } + break; + + case MODE_MOVE_ELEMENT: + { + for(auto it = m_elementList.begin(); it != m_elementList.end(); ++it) { + Element* element = *it; + // Parent's element moving... + for(int i = 0; i < (int)element->GetParentList().size(); i++) { + Element* parent = element->GetParentList()[i]; + if(parent->IsSelected()) { + element->MoveNode(parent, m_camera->ScreenToWorld(event.GetPosition())); + } + } + if(element->IsSelected()) { + element->Move(m_camera->ScreenToWorld(event.GetPosition())); + Redraw(); + } + } + } + break; + + case MODE_SELECTION_RECT: + { + wxPoint2DDouble currentPos = m_camera->ScreenToWorld(event.GetPosition()); + double x, y, w, h; + if(currentPos.m_x < m_startSelRect.m_x) { + x = currentPos.m_x; + w = m_startSelRect.m_x - currentPos.m_x; + } + else + { + x = m_startSelRect.m_x; + w = currentPos.m_x - m_startSelRect.m_x; + } + if(currentPos.m_y < m_startSelRect.m_y) { + y = currentPos.m_y; + h = m_startSelRect.m_y - currentPos.m_y; + } + else + { + y = m_startSelRect.m_y; + h = currentPos.m_y - m_startSelRect.m_y; + } + + m_selectionRect = wxRect2DDouble(x, y, w, h); + Redraw(); + } + break; + } + m_camera->UpdateMousePosition(event.GetPosition()); + UpdateStatusBar(); + event.Skip(); +} + +void Workspace::OnMiddleDown(wxMouseEvent& event) +{ + if(m_mode != MODE_INSERT) { + m_mode = MODE_DRAG; + m_camera->StartTranslation(m_camera->ScreenToWorld(event.GetPosition())); + } + UpdateStatusBar(); +} +void Workspace::OnMiddleUp(wxMouseEvent& event) +{ + if(m_mode != MODE_INSERT) { + m_mode = MODE_EDIT; + } + UpdateStatusBar(); +} +void Workspace::OnScroll(wxMouseEvent& event) +{ + if(event.GetWheelRotation() > 0) + m_camera->SetScale(event.GetPosition(), +0.05); + else + m_camera->SetScale(event.GetPosition(), -0.05); + + UpdateStatusBar(); + Redraw(); +} + +void Workspace::OnKeyDown(wxKeyEvent& event) +{ + char key = event.GetUnicodeKey(); + if(key != WXK_NONE) { + switch(key) + { + case WXK_ESCAPE: // Cancel operations. + { + if(m_mode == MODE_INSERT) { + m_elementList.pop_back(); // Removes the last element being inserted. + m_mode = MODE_EDIT; + Redraw(); + } + } + break; + case 'R': // Rotate the selected elements. + { + for(auto it = m_elementList.begin(); it != m_elementList.end(); ++it) { + Element* element = *it; + // Parent's element rotating... + for(int i = 0; i < (int)element->GetParentList().size(); i++) { + Element* parent = element->GetParentList()[i]; + if(parent->IsSelected()) { + element->RotateNode(parent); + } + } + if(element->IsSelected()) { + element->Rotate(); + } + } + Redraw(); + } + break; + case 'B': // Insert a bus. + { + if(m_mode != MODE_INSERT) { + Bus* newBus = new Bus(m_camera->ScreenToWorld(event.GetPosition())); + m_elementList.push_back(newBus); + m_mode = MODE_INSERT; + m_statusBar->SetStatusText(_("Insert Bus: Click to insert, ESC to cancel.")); + Redraw(); + } + } + break; + case 'L': // Insert a power line. + { + if(m_mode != MODE_INSERT) { + Line* newLine = new Line(); + m_elementList.push_back(newLine); + m_mode = MODE_INSERT; + m_statusBar->SetStatusText(_("Insert Line: Click on a bus, ESC to cancel.")); + Redraw(); + } + } + break; + default: + break; + } + } + + UpdateStatusBar(); + event.Skip(); +} + +void Workspace::UpdateStatusBar() +{ + switch(m_mode) + { + case MODE_DRAG: + { + m_statusBar->SetStatusText(_("MODE: DRAG"), 1); + } + break; + + case MODE_INSERT: + { + m_statusBar->SetStatusText(_("MODE: INSERT"), 1); + } + break; + + case MODE_MOVE_ELEMENT: + case MODE_MOVE_PICKBOX: + case MODE_SELECTION_RECT: + case MODE_EDIT: + { + m_statusBar->SetStatusText(wxT("")); + m_statusBar->SetStatusText(_("MODE: EDIT"), 1); + } + break; + } + + m_statusBar->SetStatusText(wxString::Format(_("ZOOM: %d%%"), (int)(m_camera->GetScale() * 100.0)), 2); + m_statusBar->SetStatusText( + wxString::Format(wxT("X: %.1f Y: %.1f"), m_camera->GetMousePosition().m_x, m_camera->GetMousePosition().m_y), + 3); +} + +void Workspace::OnPopupClick(wxCommandEvent& event) +{ + wxMenu* menu = (wxMenu*)event.GetEventObject(); + Element* element = (Element*)menu->GetClientData(); + switch(event.GetId()) + { + case ID_EDIT_BUS: + { + wxMessageBox("Edit bus!"); + } + break; + case ID_EDIT_LINE: + { + wxMessageBox("Edit line!"); + } + break; + case ID_ROTATE: + { + element->Rotate(); + for(auto it = m_elementList.begin(); it != m_elementList.end(); ++it) { + Element* nonEditedElement = *it; + // Parent's element rotating... + for(int i = 0; i < (int)nonEditedElement->GetParentList().size(); i++) { + Element* parent = nonEditedElement->GetParentList()[i]; + if(parent == element) { + nonEditedElement->RotateNode(parent); + } + } + } + Redraw(); + } + } +} |