diff --git a/common/gal/opengl/opengl_gal.cpp b/common/gal/opengl/opengl_gal.cpp index 6cb6421f92..e1859d9869 100644 --- a/common/gal/opengl/opengl_gal.cpp +++ b/common/gal/opengl/opengl_gal.cpp @@ -278,10 +278,8 @@ void OPENGL_GAL::DrawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndP VECTOR2D( lineLength, -aWidth / 2.0 ) ); // Draw line caps - drawStrokedSemiCircle( VECTOR2D( 0.0, 0.0 ), - ( aWidth + lineWidth ) / 2, M_PI / 2 ); - drawStrokedSemiCircle( VECTOR2D( lineLength, 0.0 ), - ( aWidth + lineWidth ) / 2, -M_PI / 2 ); + drawStrokedSemiCircle( VECTOR2D( 0.0, 0.0 ), aWidth / 2, M_PI / 2 ); + drawStrokedSemiCircle( VECTOR2D( lineLength, 0.0 ), aWidth / 2, -M_PI / 2 ); Restore(); } diff --git a/common/painter.cpp b/common/painter.cpp index ddff0c9820..b6181c4210 100644 --- a/common/painter.cpp +++ b/common/painter.cpp @@ -79,25 +79,3 @@ void PAINTER::SetGAL( GAL* aGal ) { m_gal = aGal; } - - -void PAINTER::DrawBrightened( const VIEW_ITEM* aItem ) -{ - BOX2I box = aItem->ViewBBox(); - - RenderTarget oldTarget = m_gal->GetTarget(); - m_gal->SetTarget( TARGET_OVERLAY ); - - m_gal->PushDepth(); - m_gal->SetLayerDepth( -1.0 ); - - // Draw an outline that marks items as brightened - m_gal->SetIsStroke( true ); - m_gal->SetLineWidth( 100000.0 ); - m_gal->SetStrokeColor( m_brightenedColor ); - - m_gal->DrawRectangle( box.GetOrigin(), box.GetOrigin() + box.GetSize() ); - m_gal->PopDepth(); - - m_gal->SetTarget( oldTarget ); -} diff --git a/common/system/jump_x86_64_sysv_elf_gas.S b/common/system/jump_x86_64_sysv_elf_gas.S index 40b65d7db6..c22f6e8038 100644 --- a/common/system/jump_x86_64_sysv_elf_gas.S +++ b/common/system/jump_x86_64_sysv_elf_gas.S @@ -41,7 +41,7 @@ .text .globl jump_fcontext .type jump_fcontext,@function -.align 16 +.align 8 jump_fcontext: movq %rbx, (%rdi) /* save RBX */ movq %r12, 0x8(%rdi) /* save R12 */ diff --git a/common/system/make_x86_64_sysv_elf_gas.S b/common/system/make_x86_64_sysv_elf_gas.S index ba040d0519..188cc931e5 100644 --- a/common/system/make_x86_64_sysv_elf_gas.S +++ b/common/system/make_x86_64_sysv_elf_gas.S @@ -40,8 +40,12 @@ .text .globl make_fcontext + +#ifndef __APPLE__ .type make_fcontext,@function -.align 16 +#endif + +.align 8 make_fcontext: leaq -0x58(%rdi), %rax /* reserve space for fcontext_t at top of context stack */ @@ -70,5 +74,8 @@ finish: xorq %rdi, %rdi /* exit code is zero */ call _exit@PLT /* exit application */ hlt -.size make_fcontext,.-make_fcontext + +#ifndef __APPLE__ +.size make_fcontext,.-make_fcontext +#endif diff --git a/common/tool/tool_dispatcher.cpp b/common/tool/tool_dispatcher.cpp index 03ff9ef9b3..ad3a38c857 100644 --- a/common/tool/tool_dispatcher.cpp +++ b/common/tool/tool_dispatcher.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include @@ -122,15 +123,6 @@ int TOOL_DISPATCHER::decodeModifiers( const wxKeyboardState* aState ) const } -wxPoint TOOL_DISPATCHER::getCurrentMousePos() const -{ - wxPoint msp = wxGetMousePosition(); - wxPoint winp = m_editFrame->GetGalCanvas()->GetScreenPosition(); - - return wxPoint( msp.x - winp.x, msp.y - winp.y ); -} - - bool TOOL_DISPATCHER::handleMouseButton( wxEvent& aEvent, int aIndex, bool aMotion ) { ButtonState* st = m_buttons[aIndex]; @@ -208,7 +200,6 @@ bool TOOL_DISPATCHER::handleMouseButton( wxEvent& aEvent, int aIndex, bool aMoti void TOOL_DISPATCHER::DispatchWxEvent( wxEvent& aEvent ) { bool motion = false, buttonEvents = false; - VECTOR2D pos; optional evt; int type = aEvent.GetEventType(); @@ -220,7 +211,8 @@ void TOOL_DISPATCHER::DispatchWxEvent( wxEvent& aEvent ) type == wxEVT_RIGHT_DOWN || type == wxEVT_RIGHT_UP || type == EVT_REFRESH_MOUSE ) { - pos = getView()->ToWorld ( getCurrentMousePos() ); + VECTOR2D screenPos = m_toolMgr->GetViewControls()->GetCursorPosition(); + VECTOR2D pos = getView()->ToWorld( screenPos ); if( pos != m_lastMousePos || type == EVT_REFRESH_MOUSE ) { motion = true; diff --git a/common/view/view.cpp b/common/view/view.cpp index ca21e29ed8..5d529f4559 100644 --- a/common/view/view.cpp +++ b/common/view/view.cpp @@ -45,8 +45,11 @@ VIEW::VIEW( bool aIsDynamic ) : m_scale( 1.0 ), m_painter( NULL ), m_gal( NULL ), - m_dynamic( aIsDynamic ) + m_dynamic( aIsDynamic ), + m_scaleLimits( 15000.0, 1.0 ) { + m_panBoundary.SetMaximum(); + // Redraw everything at the beginning for( int i = 0; i < TARGETS_NUMBER; ++i ) MarkTargetDirty( i ); @@ -290,6 +293,11 @@ void VIEW::SetScale( double aScale ) void VIEW::SetScale( double aScale, const VECTOR2D& aAnchor ) { + if( aScale > m_scaleLimits.x ) + aScale = m_scaleLimits.x; + else if( aScale < m_scaleLimits.y ) + aScale = m_scaleLimits.y; + VECTOR2D a = ToScreen( aAnchor ); m_gal->SetZoomFactor( aScale ); @@ -308,6 +316,20 @@ void VIEW::SetScale( double aScale, const VECTOR2D& aAnchor ) void VIEW::SetCenter( const VECTOR2D& aCenter ) { m_center = aCenter; + + if( !m_panBoundary.Contains( aCenter ) ) + { + if( aCenter.x < m_panBoundary.GetLeft() ) + m_center.x = m_panBoundary.GetLeft(); + else if( aCenter.x > m_panBoundary.GetRight() ) + m_center.x = m_panBoundary.GetRight(); + + if( aCenter.y < m_panBoundary.GetTop() ) + m_center.y = m_panBoundary.GetTop(); + else if( aCenter.y > m_panBoundary.GetBottom() ) + m_center.y = m_panBoundary.GetBottom(); + } + m_gal->SetLookAtPoint( m_center ); m_gal->ComputeWorldScreenMatrix(); @@ -604,12 +626,6 @@ void VIEW::draw( VIEW_ITEM* aItem, int aLayer, bool aImmediate ) const if( !m_painter->Draw( aItem, aLayer ) ) aItem->ViewDraw( aLayer, m_gal ); // Alternative drawing method } - - // Draws a bright contour around the item - if( static_cast( aItem )->IsBrightened() ) - { - m_painter->DrawBrightened( aItem ); - } } diff --git a/common/view/wx_view_controls.cpp b/common/view/wx_view_controls.cpp index cbbe289cef..4aa69d2d61 100644 --- a/common/view/wx_view_controls.cpp +++ b/common/view/wx_view_controls.cpp @@ -28,6 +28,7 @@ #include #include #include +#include using namespace KiGfx; @@ -58,18 +59,20 @@ WX_VIEW_CONTROLS::WX_VIEW_CONTROLS( VIEW* aView, wxWindow* aParentPanel ) : WX_VIEW_CONTROLS::onTimer ), NULL, this ); } -void VIEW_CONTROLS::ShowCursor ( bool aEnabled ) + +void VIEW_CONTROLS::ShowCursor( bool aEnabled ) { m_view->GetGAL()->SetCursorEnabled( aEnabled ); } + void WX_VIEW_CONTROLS::onMotion( wxMouseEvent& aEvent ) { m_mousePosition.x = aEvent.GetX(); m_mousePosition.y = aEvent.GetY(); if( m_forceCursorPosition ) - m_cursorPosition = m_view->ToScreen (m_forcedPosition); + m_cursorPosition = m_view->ToScreen( m_forcedPosition ); else if( m_snappingEnabled ) m_cursorPosition = m_view->GetGAL()->GetGridPoint( m_mousePosition ); else @@ -190,26 +193,28 @@ void WX_VIEW_CONTROLS::onTimer( wxTimerEvent& aEvent ) { switch( m_state ) { - case AUTO_PANNING: - { - double borderSize = std::min( m_autoPanMargin * m_view->GetScreenPixelSize().x, - m_autoPanMargin * m_view->GetScreenPixelSize().y ); + case AUTO_PANNING: + { + double borderSize = std::min( m_autoPanMargin * m_view->GetScreenPixelSize().x, + m_autoPanMargin * m_view->GetScreenPixelSize().y ); - VECTOR2D dir( m_panDirection ); + VECTOR2D dir( m_panDirection ); - if( dir.EuclideanNorm() > borderSize ) - dir = dir.Resize( borderSize ); + if( dir.EuclideanNorm() > borderSize ) + dir = dir.Resize( borderSize ); - dir = m_view->ToWorld( dir, false ); - m_view->SetCenter( m_view->GetCenter() + dir * m_autoPanSpeed ); - m_parentPanel->Refresh(); - - } + dir = m_view->ToWorld( dir, false ); + m_view->SetCenter( m_view->GetCenter() + dir * m_autoPanSpeed ); + + // Notify tools that the cursor position has changed in the world coordinates + wxCommandEvent moveEvent( TOOL_DISPATCHER::EVT_REFRESH_MOUSE ); + wxPostEvent( m_parentPanel, moveEvent ); + } + break; + + case IDLE: // Just remove unnecessary warnings + case DRAG_PANNING: break; - - case IDLE: // Just remove unnecessary warnings - case DRAG_PANNING: - break; } } @@ -225,6 +230,24 @@ void WX_VIEW_CONTROLS::SetGrabMouse( bool aEnabled ) } +const VECTOR2D WX_VIEW_CONTROLS::GetMousePosition() const +{ + wxPoint msp = wxGetMousePosition(); + wxPoint winp = m_parentPanel->GetScreenPosition(); + + return VECTOR2D( msp.x - winp.x, msp.y - winp.y ); +} + + +const VECTOR2D WX_VIEW_CONTROLS::GetCursorPosition() const +{ + if( m_snappingEnabled ) + return m_view->GetGAL()->GetGridPoint( GetMousePosition() ); + else + return GetMousePosition(); +} + + bool WX_VIEW_CONTROLS::handleAutoPanning( const wxMouseEvent& aEvent ) { VECTOR2D p( aEvent.GetX(), aEvent.GetY() ); diff --git a/common/worksheet_item.cpp b/common/worksheet_item.cpp index 9845e48f92..9eefc3d908 100644 --- a/common/worksheet_item.cpp +++ b/common/worksheet_item.cpp @@ -97,9 +97,6 @@ void WORKSHEET_ITEM::ViewDraw( int aLayer, GAL* aGal ) const EDA_COLOR_T edaColor = ColorFindNearest( color.r * 255, color.g * 255, color.b * 255 ); drawList.BuildWorkSheetGraphicList( *m_pageInfo, *m_titleBlock, edaColor, edaColor ); - // Draw gray line that outlines the sheet size - drawBorder( aGal ); - // Draw all the components that make the page layout WS_DRAW_ITEM_BASE* item = drawList.GetFirst(); while( item ) @@ -125,6 +122,9 @@ void WORKSHEET_ITEM::ViewDraw( int aLayer, GAL* aGal ) const item = drawList.GetNext(); } + + // Draw gray line that outlines the sheet size + drawBorder( aGal ); } @@ -200,6 +200,5 @@ void WORKSHEET_ITEM::drawBorder( GAL* aGal ) const aGal->SetIsStroke( true ); aGal->SetIsFill( false ); - aGal->SetStrokeColor( COLOR4D( 0.5, 0.5, 0.5, 1.0 ) ); aGal->DrawRectangle( origin, end ); } diff --git a/include/base_struct.h b/include/base_struct.h index b61368e0d1..9f615dc946 100644 --- a/include/base_struct.h +++ b/include/base_struct.h @@ -47,104 +47,6 @@ extern std::ostream& operator <<( std::ostream& out, const wxPoint& pt ); #endif -/** - * Enum KICAD_T - * is the set of class identification values, stored in EDA_ITEM::m_StructType - */ -enum KICAD_T { - NOT_USED = -1, ///< the 3d code uses this value - - EOT = 0, ///< search types array terminator (End Of Types) - - TYPE_NOT_INIT = 0, - PCB_T, - SCREEN_T, ///< not really an item, used to identify a screen - - // Items in pcb - PCB_MODULE_T, ///< class MODULE, a footprint - PCB_PAD_T, ///< class D_PAD, a pad in a footprint - PCB_LINE_T, ///< class DRAWSEGMENT, a segment not on copper layers - PCB_TEXT_T, ///< class TEXTE_PCB, text on a layer - PCB_MODULE_TEXT_T, ///< class TEXTE_MODULE, text in a footprint - PCB_MODULE_EDGE_T, ///< class EDGE_MODULE, a footprint edge - PCB_TRACE_T, ///< class TRACKE, a track segment (segment on a copper layer) - PCB_VIA_T, ///< class SEGVIA, a via (like a track segment on a copper layer) - PCB_ZONE_T, ///< class SEGZONE, a segment used to fill a zone area (segment on a - ///< copper layer) - PCB_MARKER_T, ///< class MARKER_PCB, a marker used to show something - PCB_DIMENSION_T, ///< class DIMENSION, a dimension (graphic item) - PCB_TARGET_T, ///< class PCB_TARGET, a target (graphic item) - PCB_ZONE_AREA_T, ///< class ZONE_CONTAINER, a zone area - PCB_ITEM_LIST_T, ///< class BOARD_ITEM_LIST, a list of board items - - // Schematic draw Items. The order of these items effects the sort order. - // It is currently ordered to mimic the old Eeschema locate behavior where - // the smallest item is the selected item. - SCH_MARKER_T, - SCH_JUNCTION_T, - SCH_NO_CONNECT_T, - SCH_BUS_WIRE_ENTRY_T, - SCH_BUS_BUS_ENTRY_T, - SCH_LINE_T, - SCH_BITMAP_T, - SCH_TEXT_T, - SCH_LABEL_T, - SCH_GLOBAL_LABEL_T, - SCH_HIERARCHICAL_LABEL_T, - SCH_FIELD_T, - SCH_COMPONENT_T, - SCH_SHEET_PIN_T, - SCH_SHEET_T, - - // Be prudent with these 3 types: - // they should be used only to locate a specific field type - // among SCH_FIELD_T items types - SCH_FIELD_LOCATE_REFERENCE_T, - SCH_FIELD_LOCATE_VALUE_T, - SCH_FIELD_LOCATE_FOOTPRINT_T, - - // General - SCH_SCREEN_T, - - /* - * Draw items in library component. - * - * The order of these items effects the sort order for items inside the - * "DRAW/ENDDRAW" section of the component definition in a library file. - * If you add a new draw item, type, please make sure you add it so the - * sort order is logical. - */ - LIB_COMPONENT_T, - LIB_ALIAS_T, - LIB_ARC_T, - LIB_CIRCLE_T, - LIB_TEXT_T, - LIB_RECTANGLE_T, - LIB_POLYLINE_T, - LIB_BEZIER_T, - LIB_PIN_T, - - /* - * Fields are not saved inside the "DRAW/ENDDRAW". Add new draw item - * types before this line. - */ - LIB_FIELD_T, - - /* - * For GerbView: items type: - */ - TYPE_GERBER_DRAW_ITEM, - - /* - * for Pl_Editor, in undo/redo commands - */ - TYPE_PL_EDITOR_LAYOUT, - - // End value - MAX_STRUCT_TYPE_ID -}; - - /** * Enum FILL_T * is the set of fill types used in plotting or drawing enclosed areas. @@ -440,12 +342,7 @@ public: EDA_ITEM( const EDA_ITEM& base ); virtual ~EDA_ITEM() { }; - /** - * Function Type - * returns the type of object. This attribute should never be changed - * after a constructor sets it, so there is no public "setter" method. - * @return KICAD_T - the type of object. - */ + /// @copydoc VIEW_ITEM::Type() KICAD_T Type() const { return m_StructType; } void SetTimeStamp( time_t aNewTimeStamp ) { m_TimeStamp = aNewTimeStamp; } @@ -474,11 +371,11 @@ public: inline void SetSelected() { SetFlags( SELECTED ); ViewUpdate( COLOR ); } inline void SetHighlighted() { SetFlags( HIGHLIGHTED ); ViewUpdate( COLOR ); } - inline void SetBrightened() { SetFlags( BRIGHTENED ); ViewUpdate( COLOR ); } + inline void SetBrightened() { SetFlags( BRIGHTENED ); } inline void ClearSelected() { ClearFlags( SELECTED ); ViewUpdate( COLOR ); } inline void ClearHighlighted() { ClearFlags( HIGHLIGHTED ); ViewUpdate( COLOR ); } - inline void ClearBrightened() { ClearFlags( BRIGHTENED ); ViewUpdate( COLOR ); } + inline void ClearBrightened() { ClearFlags( BRIGHTENED ); } void SetModified(); diff --git a/include/painter.h b/include/painter.h index 1cbd2df94b..5f9631506a 100644 --- a/include/painter.h +++ b/include/painter.h @@ -80,6 +80,16 @@ public: m_activeLayers.erase( aLayerId ); } + /** + * Function GetActiveLayers() + * Returns the set of currently active layers. + * @return The set of currently active layers. + */ + const std::set GetActiveLayers() + { + return m_activeLayers; + } + /** * Function ClearActiveLayers * Clears the list of active layers. @@ -89,6 +99,16 @@ public: m_activeLayers.clear(); } + /** + * Function IsActiveLayer + * Returns information whether the queried layer is marked as active. + * @return True if the queried layer is marked as active. + */ + inline bool IsActiveLayer( int aLayerId ) const + { + return ( m_activeLayers.count( aLayerId ) > 0 ); + } + /** * Function SetHighlight * Turns on/off highlighting - it may be done for the active layer or the specified net. @@ -114,6 +134,16 @@ public: m_hiContrastEnabled = aEnabled; } + /** + * Function GetHighContrast + * Returns information about high contrast display mode. + * @return True if the high contrast mode is on, false otherwise. + */ + inline bool GetHighContrast() const + { + return m_hiContrastEnabled; + } + /** * Function GetColor * Returns the color that should be used to draw the specific VIEW_ITEM on the specific layer @@ -224,13 +254,6 @@ public: */ virtual bool Draw( const VIEW_ITEM* aItem, int aLayer ) = 0; - /** - * Function DrawBrightened - * Draws a special marking for the item. - * @param aItem is the item that is going to be marked. - */ - virtual void DrawBrightened( const VIEW_ITEM* aItem ); - protected: /// Instance of graphic abstraction layer that gives an interface to call /// commands used to draw (eg. DrawLine, DrawCircle, etc.) diff --git a/include/tool/tool_dispatcher.h b/include/tool/tool_dispatcher.h index f5b7bacae0..cb3273f744 100644 --- a/include/tool/tool_dispatcher.h +++ b/include/tool/tool_dispatcher.h @@ -76,8 +76,6 @@ private: bool handleMouseButton( wxEvent& aEvent, int aIndex, bool aMotion ); bool handlePopupMenu( wxEvent& aEvent ); - wxPoint getCurrentMousePos() const; - int decodeModifiers( const wxKeyboardState* aState ) const; struct ButtonState; diff --git a/include/view/view.h b/include/view/view.h index 714283ef44..dc6a27691a 100644 --- a/include/view/view.h +++ b/include/view/view.h @@ -455,6 +455,29 @@ public: m_dirtyTargets[i] = true; } + /** + * Function SetPanBoundary() + * Sets limits for panning area. + * @param aBoundary is the box that limits panning area. + */ + void SetPanBoundary( const BOX2I& aBoundary ) + { + m_panBoundary = aBoundary; + } + + /** + * Function SetScaleLimits() + * Sets minimum and maximum values for scale. + * @param aMaximum is the maximum value for scale.. + * @param aMinimum is the minimum value for scale. + */ + void SetScaleLimits( double aMaximum, double aMinimum ) + { + wxASSERT_MSG( aMaximum > aMinimum, wxT( "I guess you passed parameters in wrong order" ) ); + + m_scaleLimits = VECTOR2D( aMaximum, aMinimum ); + } + static const int VIEW_MAX_LAYERS = 128; ///* maximum number of layers that may be shown private: @@ -588,6 +611,12 @@ private: /// Rendering order modifier for layers that are marked as top layers static const int TOP_LAYER_MODIFIER = -VIEW_MAX_LAYERS; + + /// Panning boundaries + BOX2I m_panBoundary; + + /// Zoom limits + VECTOR2D m_scaleLimits; }; } // namespace KiGfx diff --git a/include/view/view_controls.h b/include/view/view_controls.h index af664da11c..7bf11860cb 100644 --- a/include/view/view_controls.h +++ b/include/view/view_controls.h @@ -46,9 +46,9 @@ class VIEW; class VIEW_CONTROLS { public: - VIEW_CONTROLS( VIEW* aView ) : m_view( aView ), m_snappingEnabled( false ), - m_grabMouse( false ), m_autoPanEnabled( false ), m_autoPanMargin( 0.1 ), - m_autoPanSpeed( 0.15 ) {}; + VIEW_CONTROLS( VIEW* aView ) : m_view( aView ), m_forceCursorPosition( false ), + m_snappingEnabled( false ), m_grabMouse( false ), m_autoPanEnabled( false ), + m_autoPanMargin( 0.1 ), m_autoPanSpeed( 0.15 ) {}; virtual ~VIEW_CONTROLS() {}; /** @@ -110,10 +110,7 @@ public: * * @return The current mouse pointer position. */ - virtual const VECTOR2D& GetMousePosition() const - { - return m_mousePosition; - } + virtual const VECTOR2D GetMousePosition() const = 0; /** * Function GetCursorPosition() @@ -122,10 +119,7 @@ public: * * @return The current cursor position in screen coordinates. */ - virtual const VECTOR2D& GetCursorPosition() const - { - return m_cursorPosition; - } + virtual const VECTOR2D GetCursorPosition() const = 0; /** @@ -140,7 +134,7 @@ public: m_forceCursorPosition = aEnabled; } - virtual void ShowCursor ( bool aEnabled ); + virtual void ShowCursor( bool aEnabled ); protected: /// Pointer to controlled VIEW. @@ -155,6 +149,9 @@ protected: /// Forced cursor position VECTOR2D m_forcedPosition; + /// Is the forced cursor position enabled + bool m_forceCursorPosition; + /// Should the cursor snap to grid or move freely bool m_snappingEnabled; @@ -164,8 +161,6 @@ protected: /// Flag for turning on autopanning bool m_autoPanEnabled; - bool m_forceCursorPosition; - /// Distance from cursor to VIEW edge when panning is active float m_autoPanMargin; diff --git a/include/view/view_group.h b/include/view/view_group.h index 18f5f5a9b6..7664060560 100644 --- a/include/view/view_group.h +++ b/include/view/view_group.h @@ -142,7 +142,7 @@ public: * * @return Pointer to the VIEW instance. */ - KiGfx::VIEW *GetView() const + KiGfx::VIEW* GetView() const { return m_view; } diff --git a/include/view/view_item.h b/include/view/view_item.h index 8004c4264b..cbf8be7967 100644 --- a/include/view/view_item.h +++ b/include/view/view_item.h @@ -36,6 +36,104 @@ #include #include +/** + * Enum KICAD_T + * is the set of class identification values, stored in EDA_ITEM::m_StructType + */ +enum KICAD_T { + NOT_USED = -1, ///< the 3d code uses this value + + EOT = 0, ///< search types array terminator (End Of Types) + + TYPE_NOT_INIT = 0, + PCB_T, + SCREEN_T, ///< not really an item, used to identify a screen + + // Items in pcb + PCB_MODULE_T, ///< class MODULE, a footprint + PCB_PAD_T, ///< class D_PAD, a pad in a footprint + PCB_LINE_T, ///< class DRAWSEGMENT, a segment not on copper layers + PCB_TEXT_T, ///< class TEXTE_PCB, text on a layer + PCB_MODULE_TEXT_T, ///< class TEXTE_MODULE, text in a footprint + PCB_MODULE_EDGE_T, ///< class EDGE_MODULE, a footprint edge + PCB_TRACE_T, ///< class TRACKE, a track segment (segment on a copper layer) + PCB_VIA_T, ///< class SEGVIA, a via (like a track segment on a copper layer) + PCB_ZONE_T, ///< class SEGZONE, a segment used to fill a zone area (segment on a + ///< copper layer) + PCB_MARKER_T, ///< class MARKER_PCB, a marker used to show something + PCB_DIMENSION_T, ///< class DIMENSION, a dimension (graphic item) + PCB_TARGET_T, ///< class PCB_TARGET, a target (graphic item) + PCB_ZONE_AREA_T, ///< class ZONE_CONTAINER, a zone area + PCB_ITEM_LIST_T, ///< class BOARD_ITEM_LIST, a list of board items + + // Schematic draw Items. The order of these items effects the sort order. + // It is currently ordered to mimic the old Eeschema locate behavior where + // the smallest item is the selected item. + SCH_MARKER_T, + SCH_JUNCTION_T, + SCH_NO_CONNECT_T, + SCH_BUS_WIRE_ENTRY_T, + SCH_BUS_BUS_ENTRY_T, + SCH_LINE_T, + SCH_BITMAP_T, + SCH_TEXT_T, + SCH_LABEL_T, + SCH_GLOBAL_LABEL_T, + SCH_HIERARCHICAL_LABEL_T, + SCH_FIELD_T, + SCH_COMPONENT_T, + SCH_SHEET_PIN_T, + SCH_SHEET_T, + + // Be prudent with these 3 types: + // they should be used only to locate a specific field type + // among SCH_FIELD_T items types + SCH_FIELD_LOCATE_REFERENCE_T, + SCH_FIELD_LOCATE_VALUE_T, + SCH_FIELD_LOCATE_FOOTPRINT_T, + + // General + SCH_SCREEN_T, + + /* + * Draw items in library component. + * + * The order of these items effects the sort order for items inside the + * "DRAW/ENDDRAW" section of the component definition in a library file. + * If you add a new draw item, type, please make sure you add it so the + * sort order is logical. + */ + LIB_COMPONENT_T, + LIB_ALIAS_T, + LIB_ARC_T, + LIB_CIRCLE_T, + LIB_TEXT_T, + LIB_RECTANGLE_T, + LIB_POLYLINE_T, + LIB_BEZIER_T, + LIB_PIN_T, + + /* + * Fields are not saved inside the "DRAW/ENDDRAW". Add new draw item + * types before this line. + */ + LIB_FIELD_T, + + /* + * For GerbView: items type: + */ + TYPE_GERBER_DRAW_ITEM, + + /* + * for Pl_Editor, in undo/redo commands + */ + TYPE_PL_EDITOR_LAYOUT, + + // End value + MAX_STRUCT_TYPE_ID +}; + + namespace KiGfx { // Forward declarations @@ -81,6 +179,17 @@ public: delete[] m_groups; }; + /** + * Function Type + * returns the type of object. This attribute should never be changed + * after a constructor sets it, so there is no public "setter" method. + * @return KICAD_T - the type of object. + */ + virtual KICAD_T Type() const + { + return NOT_USED; + } + /** * Function ViewBBox() * returns the bounding box of the item covering all its layers. diff --git a/include/view/wx_view_controls.h b/include/view/wx_view_controls.h index 7c905f2f9c..8562afbb82 100644 --- a/include/view/wx_view_controls.h +++ b/include/view/wx_view_controls.h @@ -78,6 +78,12 @@ public: m_state = IDLE; } + /// @copydoc VIEW_CONTROLS::GetMousePosition() + virtual const VECTOR2D GetMousePosition() const; + + /// @copydoc VIEW_CONTROLS::GetCursorPosition() + virtual const VECTOR2D GetCursorPosition() const; + private: /// Possible states for WX_VIEW_CONTROLS enum State { diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index 02c943d0d9..bce6beeaa3 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -221,6 +221,7 @@ set(PCBNEW_CLASS_SRCS tools/selection_tool.cpp tools/selection_area.cpp + tools/bright_box.cpp tools/move_tool.cpp tools/pcb_tools.cpp ) diff --git a/pcbnew/basepcbframe.cpp b/pcbnew/basepcbframe.cpp index 9692ba9738..a6c0ec42ec 100644 --- a/pcbnew/basepcbframe.cpp +++ b/pcbnew/basepcbframe.cpp @@ -217,6 +217,7 @@ void PCB_BASE_FRAME::ViewReloadBoard( const BOARD* aBoard ) const view->Add( worksheet ); + view->SetPanBoundary( worksheet->ViewBBox() ); view->RecacheAllItems( true ); if( m_galCanvasActive ) @@ -857,8 +858,17 @@ void PCB_BASE_FRAME::LoadSettings() view->SetRequired( ITEM_GAL_LAYER( VIAS_HOLES_VISIBLE ), ITEM_GAL_LAYER( VIAS_VISIBLE ) ); view->SetRequired( ITEM_GAL_LAYER( PADS_HOLES_VISIBLE ), ITEM_GAL_LAYER( PADS_VISIBLE ) ); view->SetRequired( ITEM_GAL_LAYER( PADS_NETNAMES_VISIBLE ), ITEM_GAL_LAYER( PADS_VISIBLE ) ); + view->SetRequired( ITEM_GAL_LAYER( PAD_FR_NETNAMES_VISIBLE ), ITEM_GAL_LAYER( PAD_FR_VISIBLE ) ); + view->SetRequired( ADHESIVE_N_FRONT, ITEM_GAL_LAYER( PAD_FR_VISIBLE ) ); + view->SetRequired( SOLDERPASTE_N_FRONT, ITEM_GAL_LAYER( PAD_FR_VISIBLE ) ); + view->SetRequired( SOLDERMASK_N_FRONT, ITEM_GAL_LAYER( PAD_FR_VISIBLE ) ); + view->SetRequired( ITEM_GAL_LAYER( PAD_BK_NETNAMES_VISIBLE ), ITEM_GAL_LAYER( PAD_BK_VISIBLE ) ); + view->SetRequired( ADHESIVE_N_BACK, ITEM_GAL_LAYER( PAD_BK_VISIBLE ) ); + view->SetRequired( SOLDERPASTE_N_BACK, ITEM_GAL_LAYER( PAD_BK_VISIBLE ) ); + view->SetRequired( SOLDERMASK_N_BACK, ITEM_GAL_LAYER( PAD_BK_VISIBLE ) ); + view->SetLayerTarget( ITEM_GAL_LAYER( SELECTION ), KiGfx::TARGET_OVERLAY ); view->SetLayerTarget( ITEM_GAL_LAYER( GP_OVERLAY ), KiGfx::TARGET_OVERLAY ); diff --git a/pcbnew/class_dimension.cpp b/pcbnew/class_dimension.cpp index c6dab49476..f7dacdc2a6 100644 --- a/pcbnew/class_dimension.cpp +++ b/pcbnew/class_dimension.cpp @@ -468,6 +468,15 @@ EDA_RECT DIMENSION::GetBoundingBox() const ymax = std::max( ymax, m_featureLineGO.y ); ymax = std::max( ymax, m_featureLineGF.y ); + xmin = std::min( xmin, m_featureLineDO.x ); + xmin = std::min( xmin, m_featureLineDF.x ); + ymin = std::min( ymin, m_featureLineDO.y ); + ymin = std::min( ymin, m_featureLineDF.y ); + xmax = std::max( xmax, m_featureLineDO.x ); + xmax = std::max( xmax, m_featureLineDF.x ); + ymax = std::max( ymax, m_featureLineDO.y ); + ymax = std::max( ymax, m_featureLineDF.y ); + bBox.SetX( xmin ); bBox.SetY( ymin ); bBox.SetWidth( xmax - xmin + 1 ); @@ -489,6 +498,16 @@ wxString DIMENSION::GetSelectMenuText() const } +const BOX2I DIMENSION::ViewBBox() const +{ + BOX2I dimBBox = BOX2I( VECTOR2I( GetBoundingBox().GetPosition() ), + VECTOR2I( GetBoundingBox().GetSize() ) ); + dimBBox.Merge( m_Text.ViewBBox() ); + + return dimBBox; +} + + void DIMENSION::ViewGetLayers( int aLayers[], int& aCount ) const { // Layer that simply displays the text diff --git a/pcbnew/class_dimension.h b/pcbnew/class_dimension.h index c92aae7433..25baeca80f 100644 --- a/pcbnew/class_dimension.h +++ b/pcbnew/class_dimension.h @@ -144,6 +144,9 @@ public: EDA_ITEM* Clone() const; + /// @copydoc VIEW_ITEM::ViewBBox() + virtual const BOX2I ViewBBox() const; + /// @copydoc VIEW_ITEM::ViewGetLayers() virtual void ViewGetLayers( int aLayers[], int& aCount ) const; diff --git a/pcbnew/class_module.cpp b/pcbnew/class_module.cpp index 0b68f61e64..680be27848 100644 --- a/pcbnew/class_module.cpp +++ b/pcbnew/class_module.cpp @@ -1027,6 +1027,7 @@ void MODULE::SetOrientation( double newangle ) void MODULE::ViewGetLayers( int aLayers[], int& aCount ) const { - aCount = 1; + aCount = 2; aLayers[0] = ITEM_GAL_LAYER( SELECTION ); // Selection box + aLayers[1] = m_Layer; } diff --git a/pcbnew/pcb_painter.cpp b/pcbnew/pcb_painter.cpp index 3889963c3e..dbc023eba7 100644 --- a/pcbnew/pcb_painter.cpp +++ b/pcbnew/pcb_painter.cpp @@ -76,6 +76,7 @@ void PCB_RENDER_SETTINGS::ImportLegacyColors( COLORS_DESIGN_SETTINGS* aSettings m_layerColors[ITEM_GAL_LAYER( PAD_FR_NETNAMES_VISIBLE )] = COLOR4D( 0.8, 0.8, 0.8, 0.7 ); m_layerColors[ITEM_GAL_LAYER( PAD_BK_NETNAMES_VISIBLE )] = COLOR4D( 0.8, 0.8, 0.8, 0.7 ); m_layerColors[ITEM_GAL_LAYER( WORKSHEET )] = COLOR4D( 0.5, 0.0, 0.0, 1.0 ); + m_layerColors[ITEM_GAL_LAYER( SELECTION )] = COLOR4D( 1.0, 1.0, 1.0, 0.5 ); // Netnames for copper layers for( LAYER_NUM layer = FIRST_COPPER_LAYER; layer <= LAST_COPPER_LAYER; ++layer ) @@ -205,10 +206,8 @@ PCB_PAINTER::PCB_PAINTER( GAL* aGal ) : bool PCB_PAINTER::Draw( const VIEW_ITEM* aItem, int aLayer ) { - const BOARD_ITEM* item = static_cast( aItem ); - // the "cast" applied in here clarifies which overloaded draw() is called - switch( item->Type() ) + switch( aItem->Type() ) { case PCB_ZONE_T: case PCB_TRACE_T: @@ -229,7 +228,7 @@ bool PCB_PAINTER::Draw( const VIEW_ITEM* aItem, int aLayer ) break; case PCB_MODULE_T: - draw( (MODULE*) aItem ); + draw( (MODULE*) aItem, aLayer ); break; case PCB_TEXT_T: @@ -273,7 +272,7 @@ void PCB_PAINTER::draw( const TRACK* aTrack, int aLayer ) if( m_pcbSettings->m_netNamesOnTracks && IsNetnameLayer( aLayer ) ) { // If there is a net name - display it on the track - if( netNumber != 0 ) + if( netNumber > 0 ) { VECTOR2D line = ( end - start ); double length = line.EuclideanNorm(); @@ -283,7 +282,7 @@ void PCB_PAINTER::draw( const TRACK* aTrack, int aLayer ) return; NETINFO_ITEM* net = ( (BOARD*) aTrack->GetParent() )->FindNet( netNumber ); - if(!net) + if( !net ) return; std::string netName = std::string( net->GetShortNetname().mb_str() ); @@ -631,7 +630,7 @@ void PCB_PAINTER::draw( const D_PAD* aPad, int aLayer ) void PCB_PAINTER::draw( const DRAWSEGMENT* aSegment ) { - COLOR4D color = m_pcbSettings->GetColor( NULL, aSegment->GetLayer() ); + COLOR4D color = m_pcbSettings->GetColor( aSegment, aSegment->GetLayer() ); m_gal->SetIsFill( false ); m_gal->SetIsStroke( true ); @@ -700,11 +699,14 @@ void PCB_PAINTER::draw( const DRAWSEGMENT* aSegment ) } -void PCB_PAINTER::draw( const MODULE* aModule ) +void PCB_PAINTER::draw( const MODULE* aModule, int aLayer ) { // For modules we have to draw a selection box if needed - if( aModule->IsSelected() ) - drawSelectionBox( aModule ); + if( aLayer == ITEM_GAL_LAYER( SELECTION ) ) + { + if( aModule->IsSelected() ) + drawSelectionBox( aModule ); + } } @@ -720,7 +722,7 @@ void PCB_PAINTER::draw( const TEXTE_PCB* aText, int aLayer ) if( aText->GetText().Length() == 0 ) return; - COLOR4D strokeColor = m_pcbSettings->GetColor( NULL, aText->GetLayer() ); + COLOR4D strokeColor = m_pcbSettings->GetColor( aText, aText->GetLayer() ); VECTOR2D position( aText->GetTextPosition().x, aText->GetTextPosition().y ); double orientation = aText->GetOrientation() * M_PI / 1800.0; @@ -744,7 +746,7 @@ void PCB_PAINTER::draw( const TEXTE_MODULE* aText, int aLayer ) if( aText->GetLength() == 0 ) return; - COLOR4D strokeColor = m_pcbSettings->GetColor( NULL, aLayer ); + COLOR4D strokeColor = m_pcbSettings->GetColor( aText, aLayer ); VECTOR2D position( aText->GetTextPosition().x, aText->GetTextPosition().y ); double orientation = aText->GetDrawRotation() * M_PI / 1800.0; @@ -759,7 +761,7 @@ void PCB_PAINTER::draw( const TEXTE_MODULE* aText, int aLayer ) void PCB_PAINTER::draw( const ZONE_CONTAINER* aZone ) { - COLOR4D color = m_pcbSettings->GetColor( NULL, aZone->GetLayer() ); + COLOR4D color = m_pcbSettings->GetColor( aZone, aZone->GetLayer() ); std::deque corners; PCB_RENDER_SETTINGS::DisplayZonesMode displayMode = m_pcbSettings->m_displayZoneMode; @@ -836,7 +838,7 @@ void PCB_PAINTER::draw( const DIMENSION* aDimension, int aLayer ) else { int layer = aDimension->GetLayer(); - COLOR4D strokeColor = m_pcbSettings->GetColor( NULL, layer ); + COLOR4D strokeColor = m_pcbSettings->GetColor( aDimension, layer ); m_gal->SetStrokeColor( strokeColor ); m_gal->SetIsFill( false ); @@ -855,14 +857,20 @@ void PCB_PAINTER::draw( const DIMENSION* aDimension, int aLayer ) m_gal->DrawLine( VECTOR2D( aDimension->m_arrowG2O ), VECTOR2D( aDimension->m_arrowG2F ) ); // Draw text - draw( &aDimension->Text(), layer ); + TEXTE_PCB& text = aDimension->Text(); + VECTOR2D position( text.GetTextPosition().x, text.GetTextPosition().y ); + double orientation = text.GetOrientation() * M_PI / 1800.0; + + m_gal->SetLineWidth( text.GetThickness() ); + m_gal->SetTextAttributes( &text ); + m_gal->StrokeText( std::string( text.GetText().mb_str() ), position, orientation ); } } void PCB_PAINTER::draw( const PCB_TARGET* aTarget ) { - COLOR4D strokeColor = m_pcbSettings->GetColor( NULL, aTarget->GetLayer() ); + COLOR4D strokeColor = m_pcbSettings->GetColor( aTarget, aTarget->GetLayer() ); VECTOR2D position( aTarget->GetPosition() ); double size, radius; @@ -888,10 +896,8 @@ void PCB_PAINTER::draw( const PCB_TARGET* aTarget ) radius = aTarget->GetSize() / 3.0; } - m_gal->DrawLine( VECTOR2D( -size, 0.0 ), - VECTOR2D( size, 0.0 ) ); - m_gal->DrawLine( VECTOR2D( 0.0, -size ), - VECTOR2D( 0.0, size ) ); + m_gal->DrawLine( VECTOR2D( -size, 0.0 ), VECTOR2D( size, 0.0 ) ); + m_gal->DrawLine( VECTOR2D( 0.0, -size ), VECTOR2D( 0.0, size ) ); m_gal->DrawCircle( VECTOR2D( 0.0, 0.0 ), radius ); m_gal->Restore(); @@ -904,6 +910,6 @@ void PCB_PAINTER::drawSelectionBox( const VIEW_ITEM* aItem ) const m_gal->SetIsStroke( false ); m_gal->SetIsFill( true ); - m_gal->SetFillColor( COLOR4D( 1.0, 1.0, 1.0, 0.5 ) ); + m_gal->SetFillColor( m_pcbSettings->GetLayerColor( ITEM_GAL_LAYER( SELECTION ) ) ); m_gal->DrawRectangle( boundingBox.GetOrigin(), boundingBox.GetEnd() ); } diff --git a/pcbnew/pcb_painter.h b/pcbnew/pcb_painter.h index 2e106f2b93..6899c54979 100644 --- a/pcbnew/pcb_painter.h +++ b/pcbnew/pcb_painter.h @@ -145,7 +145,7 @@ protected: void draw( const SEGVIA*, int ); void draw( const D_PAD*, int ); void draw( const DRAWSEGMENT* ); - void draw( const MODULE* ); + void draw( const MODULE*, int ); void draw( const TEXTE_PCB*, int ); void draw( const TEXTE_MODULE*, int ); void draw( const ZONE_CONTAINER* ); diff --git a/pcbnew/pcbframe.cpp b/pcbnew/pcbframe.cpp index bf15c21989..e581f53a32 100644 --- a/pcbnew/pcbframe.cpp +++ b/pcbnew/pcbframe.cpp @@ -767,8 +767,7 @@ void PCB_EDIT_FRAME::setHighContrastLayer( LAYER_NUM aLayer ) LAYER_NUM layers[] = { GetNetnameLayer( aLayer ), ITEM_GAL_LAYER( VIAS_VISIBLE ), ITEM_GAL_LAYER( VIAS_HOLES_VISIBLE ), ITEM_GAL_LAYER( PADS_VISIBLE ), - ITEM_GAL_LAYER( PADS_HOLES_VISIBLE ), ITEM_GAL_LAYER( PADS_NETNAMES_VISIBLE ), - ITEM_GAL_LAYER( SELECTION ) + ITEM_GAL_LAYER( PADS_HOLES_VISIBLE ), ITEM_GAL_LAYER( PADS_NETNAMES_VISIBLE ) }; for( unsigned int i = 0; i < sizeof( layers ) / sizeof( LAYER_NUM ); ++i ) diff --git a/pcbnew/tools/bright_box.cpp b/pcbnew/tools/bright_box.cpp new file mode 100644 index 0000000000..f1c0802522 --- /dev/null +++ b/pcbnew/tools/bright_box.cpp @@ -0,0 +1,72 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Maciej Suminski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "bright_box.h" +#include +#include + +using namespace KiGfx; + +BRIGHT_BOX::BRIGHT_BOX( BOARD_ITEM* aItem ) : + EDA_ITEM( NOT_USED ), // this item is never added to a BOARD so it needs no type + item( aItem ) +{ +} + + +const BOX2I BRIGHT_BOX::ViewBBox() const +{ + return item->ViewBBox(); +} + + +void BRIGHT_BOX::ViewGetLayers( int aLayers[], int& aCount ) const +{ + aLayers[0] = BrightBoxLayer; + aCount = 1; +} + + +void BRIGHT_BOX::ViewDraw( int aLayer, GAL* aGal ) const +{ + aGal->SetIsStroke( true ); + aGal->SetIsFill( false ); + aGal->SetLineWidth( LineWidth ); + aGal->SetStrokeColor( BrightColor ); + + if( item->Type() == PCB_TRACE_T ) + { + const TRACK* track = static_cast( item ); + + aGal->DrawSegment( track->GetStart(), track->GetEnd(), track->GetWidth() ); + } + else + { + BOX2I box = item->ViewBBox(); + + aGal->DrawRectangle( box.GetOrigin(), box.GetOrigin() + box.GetSize() ); + } +} + +const COLOR4D BRIGHT_BOX::BrightColor = KiGfx::COLOR4D( 0.0, 1.0, 0.0, 1.0 ); diff --git a/pcbnew/tools/bright_box.h b/pcbnew/tools/bright_box.h new file mode 100644 index 0000000000..e1748dbd28 --- /dev/null +++ b/pcbnew/tools/bright_box.h @@ -0,0 +1,62 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Maciej Suminski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __BRIGHT_BOX_H +#define __BRIGHT_BOX_H + +#include +#include +#include +#include +#include + +/** + * Class BRIGHT_BOX + * + * Draws a decoration to indicate a brightened item. + */ +class BRIGHT_BOX : public EDA_ITEM +{ +public: + BRIGHT_BOX( BOARD_ITEM* aItem ); + ~BRIGHT_BOX() {}; + + virtual const BOX2I ViewBBox() const; + + void ViewDraw( int aLayer, KiGfx::GAL* aGal ) const; + void ViewGetLayers( int aLayers[], int& aCount ) const; + + void Show( int x, std::ostream& st ) const + { + } + +private: + static const int BrightBoxLayer = ITEM_GAL_LAYER( SELECTION ); + static const KiGfx::COLOR4D BrightColor; + static const double LineWidth = 100000.0; + + BOARD_ITEM* item; +}; + +#endif diff --git a/pcbnew/tools/selection_area.cpp b/pcbnew/tools/selection_area.cpp index 33d73d6d68..c96751cb31 100644 --- a/pcbnew/tools/selection_area.cpp +++ b/pcbnew/tools/selection_area.cpp @@ -22,11 +22,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#include - -#include - #include "selection_area.h" +#include +#include using namespace KiGfx; @@ -47,7 +45,7 @@ void SELECTION_AREA::ViewGetLayers( int aLayers[], int& aCount ) const } -void SELECTION_AREA::ViewDraw( int aLayer, GAL* aGal ) const +void SELECTION_AREA::ViewDraw( int aLayer, KiGfx::GAL* aGal ) const { aGal->SetLineWidth( 1.0 ); aGal->SetStrokeColor( COLOR4D( 1.0, 1.0, 0.4, 1.0 ) ); diff --git a/pcbnew/tools/selection_area.h b/pcbnew/tools/selection_area.h index 50db94f927..10079dccd5 100644 --- a/pcbnew/tools/selection_area.h +++ b/pcbnew/tools/selection_area.h @@ -25,15 +25,14 @@ #ifndef __SELECTION_AREA_H #define __SELECTION_AREA_H -#include -#include - -#include - -#include -#include #include #include +#include + +namespace KiGfx +{ +class GAL; +} /** * Class SELECTION_AREA diff --git a/pcbnew/tools/selection_tool.cpp b/pcbnew/tools/selection_tool.cpp index cd1a4be5a1..8559e81f6a 100644 --- a/pcbnew/tools/selection_tool.cpp +++ b/pcbnew/tools/selection_tool.cpp @@ -34,11 +34,15 @@ #include #include #include +#include #include +#include +#include #include "selection_tool.h" #include "selection_area.h" +#include "bright_box.h" using namespace KiGfx; using boost::optional; @@ -115,7 +119,8 @@ int SELECTION_TOOL::Main( TOOL_EVENT& aEvent ) // Check if dragging event started within the currently selected items bounding box std::set::iterator it, it_end; - for( it = m_selectedItems.begin(), it_end = m_selectedItems.end(); it != it_end; ++it ) + for( it = m_selectedItems.begin(), it_end = m_selectedItems.end(); + it != it_end; ++it ) { BOX2I itemBox = (*it)->ViewBBox(); itemBox.Inflate( 500000 ); // Give some margin for gripping an item @@ -204,12 +209,12 @@ void SELECTION_TOOL::selectSingle( const VECTOR2I& aWhere ) break; default: - // Remove footprints, they have to be selected by clicking on area that does not - // contain anything but footprint - for( int i = 0; i < collector.GetCount(); ++i ) + // Remove modules, they have to be selected by clicking on area that does not + // contain anything but module footprint and not selectable items + for( int i = collector.GetCount() - 1; i >= 0 ; --i ) { BOARD_ITEM* boardItem = ( collector )[i]; - if( boardItem->Type() == PCB_MODULE_T ) + if( boardItem->Type() == PCB_MODULE_T || !selectable( boardItem ) ) collector.Remove( i ); } @@ -218,7 +223,7 @@ void SELECTION_TOOL::selectSingle( const VECTOR2I& aWhere ) { toggleSelection( collector[0] ); } - else + else if( collector.GetCount() > 1 ) { item = disambiguationMenu( &collector ); if( item ) @@ -332,8 +337,8 @@ bool SELECTION_TOOL::selectMultiple() BOARD_ITEM* SELECTION_TOOL::disambiguationMenu( GENERAL_COLLECTOR* aCollector ) { - OPT_TOOL_EVENT evt; BOARD_ITEM* current = NULL; + boost::shared_ptr brightBox; m_menu.reset( new CONTEXT_MENU() ); m_menu->SetTitle( _( "Clarify selection" ) ); @@ -350,10 +355,11 @@ BOARD_ITEM* SELECTION_TOOL::disambiguationMenu( GENERAL_COLLECTOR* aCollector ) SetContextMenu( m_menu.get(), CMENU_NOW ); - while( evt = Wait() ) + while( OPT_TOOL_EVENT evt = Wait() ) { if( evt->Action() == TA_ContextMenuUpdate ) { + // User has pointed an item, so show it in a different way if( current ) current->ClearBrightened(); @@ -378,21 +384,51 @@ BOARD_ITEM* SELECTION_TOOL::disambiguationMenu( GENERAL_COLLECTOR* aCollector ) { current = ( *aCollector )[*id]; current->SetSelected(); - return current; } - return NULL; + break; + } + + if( current && current->IsBrightened() ) + { + brightBox.reset( new BRIGHT_BOX( current ) ); + getView()->Add( brightBox.get() ); } } - return NULL; + getView()->MarkTargetDirty( TARGET_OVERLAY ); + return current; } bool SELECTION_TOOL::selectable( const BOARD_ITEM* aItem ) { - BOARD* board = getModel( PCB_T ); + bool highContrast = getView()->GetPainter()->GetSettings()->GetHighContrast(); + if( highContrast ) + { + bool onActive = false; + int layers[KiGfx::VIEW::VIEW_MAX_LAYERS], layers_count; + + // Filter out items that do not belong to active layers + std::set activeLayers = getView()->GetPainter()-> + GetSettings()->GetActiveLayers(); + aItem->ViewGetLayers( layers, layers_count ); + + for( int i = 0; i < layers_count; ++i ) + { + if( activeLayers.count( layers[i] ) > 0 ) // Item is on at least one active layer + { + onActive = true; + break; + } + } + + if( !onActive ) + return false; + } + + BOARD* board = getModel( PCB_T ); switch( aItem->Type() ) { case PCB_VIA_T: diff --git a/polygon/poly2tri/common/utils.h b/polygon/poly2tri/common/utils.h index ad27efa85b..78416f2f31 100644 --- a/polygon/poly2tri/common/utils.h +++ b/polygon/poly2tri/common/utils.h @@ -1,123 +1,123 @@ -/* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * Neither the name of Poly2Tri nor the names of its contributors may be - * used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef UTILS_H -#define UTILS_H - -// Otherwise #defines like M_PI are undeclared under Visual Studio -#define _USE_MATH_DEFINES - -#include -#include - -namespace p2t { - -const double PI_3div4 = 3 * M_PI / 4; -const double PI_div2 = 1.57079632679489661923; -const double EPSILON = 1e-12; - -enum Orientation { CW, CCW, COLLINEAR }; - -/** - * Forumla to calculate signed area
- * Positive if CCW
- * Negative if CW
- * 0 if collinear
- *
- * A[P1,P2,P3]  =  (x1*y2 - y1*x2) + (x2*y3 - y2*x3) + (x3*y1 - y3*x1)
- *              =  (x1-x3)*(y2-y3) - (y1-y3)*(x2-x3)
- * 
- */ -Orientation Orient2d(Point& pa, Point& pb, Point& pc) -{ - double detleft = (pa.x - pc.x) * (pb.y - pc.y); - double detright = (pa.y - pc.y) * (pb.x - pc.x); - double val = detleft - detright; - if (val > -EPSILON && val < EPSILON) { - return COLLINEAR; - } else if (val > 0) { - return CCW; - } - return CW; -} - -/* -bool InScanArea(Point& pa, Point& pb, Point& pc, Point& pd) -{ - double pdx = pd.x; - double pdy = pd.y; - double adx = pa.x - pdx; - double ady = pa.y - pdy; - double bdx = pb.x - pdx; - double bdy = pb.y - pdy; - - double adxbdy = adx * bdy; - double bdxady = bdx * ady; - double oabd = adxbdy - bdxady; - - if (oabd <= EPSILON) { - return false; - } - - double cdx = pc.x - pdx; - double cdy = pc.y - pdy; - - double cdxady = cdx * ady; - double adxcdy = adx * cdy; - double ocad = cdxady - adxcdy; - - if (ocad <= EPSILON) { - return false; - } - - return true; -} - -*/ - -bool InScanArea(Point& pa, Point& pb, Point& pc, Point& pd) -{ - double oadb = (pa.x - pb.x)*(pd.y - pb.y) - (pd.x - pb.x)*(pa.y - pb.y); - if (oadb >= -EPSILON) { - return false; - } - - double oadc = (pa.x - pc.x)*(pd.y - pc.y) - (pd.x - pc.x)*(pa.y - pc.y); - if (oadc <= EPSILON) { - return false; - } - return true; -} - -} - -#endif - +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UTILS_H +#define UTILS_H + +// Otherwise #defines like M_PI are undeclared under Visual Studio +#define _USE_MATH_DEFINES + +#include +#include + +namespace p2t { + +const double PI_3div4 = 3 * M_PI / 4; +const double PI_div2 = 1.57079632679489661923; +const double EPSILON = 1e-12; + +enum Orientation { CW, CCW, COLLINEAR }; + +/** + * Forumla to calculate signed area
+ * Positive if CCW
+ * Negative if CW
+ * 0 if collinear
+ *
+ * A[P1,P2,P3]  =  (x1*y2 - y1*x2) + (x2*y3 - y2*x3) + (x3*y1 - y3*x1)
+ *              =  (x1-x3)*(y2-y3) - (y1-y3)*(x2-x3)
+ * 
+ */ +Orientation Orient2d(Point& pa, Point& pb, Point& pc) +{ + double detleft = (pa.x - pc.x) * (pb.y - pc.y); + double detright = (pa.y - pc.y) * (pb.x - pc.x); + double val = detleft - detright; + if (val > -EPSILON && val < EPSILON) { + return COLLINEAR; + } else if (val > 0) { + return CCW; + } + return CW; +} + +/* +bool InScanArea(Point& pa, Point& pb, Point& pc, Point& pd) +{ + double pdx = pd.x; + double pdy = pd.y; + double adx = pa.x - pdx; + double ady = pa.y - pdy; + double bdx = pb.x - pdx; + double bdy = pb.y - pdy; + + double adxbdy = adx * bdy; + double bdxady = bdx * ady; + double oabd = adxbdy - bdxady; + + if (oabd <= EPSILON) { + return false; + } + + double cdx = pc.x - pdx; + double cdy = pc.y - pdy; + + double cdxady = cdx * ady; + double adxcdy = adx * cdy; + double ocad = cdxady - adxcdy; + + if (ocad <= EPSILON) { + return false; + } + + return true; +} + +*/ + +bool InScanArea(Point& pa, Point& pb, Point& pc, Point& pd) +{ + double oadb = (pa.x - pb.x)*(pd.y - pb.y) - (pd.x - pb.x)*(pa.y - pb.y); + if (oadb >= -EPSILON) { + return false; + } + + double oadc = (pa.x - pc.x)*(pd.y - pc.y) - (pd.x - pc.x)*(pa.y - pc.y); + if (oadc <= EPSILON) { + return false; + } + return true; +} + +} + +#endif + diff --git a/polygon/poly2tri/sweep/cdt.cc b/polygon/poly2tri/sweep/cdt.cc index f013e47e37..d7838257c7 100644 --- a/polygon/poly2tri/sweep/cdt.cc +++ b/polygon/poly2tri/sweep/cdt.cc @@ -1,72 +1,72 @@ -/* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * Neither the name of Poly2Tri nor the names of its contributors may be - * used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#include "cdt.h" - -namespace p2t { - -CDT::CDT(std::vector polyline) -{ - sweep_context_ = new SweepContext(polyline); - sweep_ = new Sweep; -} - -void CDT::AddHole(std::vector polyline) -{ - sweep_context_->AddHole(polyline); -} - -void CDT::AddPoint(Point* point) { - sweep_context_->AddPoint(point); -} - -void CDT::Triangulate() -{ - sweep_->Triangulate(*sweep_context_); -} - -std::vector CDT::GetTriangles() -{ - return sweep_context_->GetTriangles(); -} - -std::list CDT::GetMap() -{ - return sweep_context_->GetMap(); -} - -CDT::~CDT() -{ - delete sweep_context_; - delete sweep_; -} - -} - +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "cdt.h" + +namespace p2t { + +CDT::CDT(std::vector polyline) +{ + sweep_context_ = new SweepContext(polyline); + sweep_ = new Sweep; +} + +void CDT::AddHole(std::vector polyline) +{ + sweep_context_->AddHole(polyline); +} + +void CDT::AddPoint(Point* point) { + sweep_context_->AddPoint(point); +} + +void CDT::Triangulate() +{ + sweep_->Triangulate(*sweep_context_); +} + +std::vector CDT::GetTriangles() +{ + return sweep_context_->GetTriangles(); +} + +std::list CDT::GetMap() +{ + return sweep_context_->GetMap(); +} + +CDT::~CDT() +{ + delete sweep_context_; + delete sweep_; +} + +} + diff --git a/polygon/poly2tri/sweep/cdt.h b/polygon/poly2tri/sweep/cdt.h index baf6ca7ddd..3e6f024086 100644 --- a/polygon/poly2tri/sweep/cdt.h +++ b/polygon/poly2tri/sweep/cdt.h @@ -1,105 +1,105 @@ -/* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * Neither the name of Poly2Tri nor the names of its contributors may be - * used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef CDT_H -#define CDT_H - -#include "advancing_front.h" -#include "sweep_context.h" -#include "sweep.h" - -/** - * - * @author Mason Green - * - */ - -namespace p2t { - -class CDT -{ -public: - - /** - * Constructor - add polyline with non repeating points - * - * @param polyline - */ - CDT(std::vector polyline); - - /** - * Destructor - clean up memory - */ - ~CDT(); - - /** - * Add a hole - * - * @param polyline - */ - void AddHole(std::vector polyline); - - /** - * Add a steiner point - * - * @param point - */ - void AddPoint(Point* point); - - /** - * Triangulate - do this AFTER you've added the polyline, holes, and Steiner points - */ - void Triangulate(); - - /** - * Get CDT triangles - */ - std::vector GetTriangles(); - - /** - * Get triangle map - */ - std::list GetMap(); - - private: - - /** - * Internals - */ - - SweepContext* sweep_context_; - Sweep* sweep_; - -}; - -} - -#endif +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CDT_H +#define CDT_H + +#include "advancing_front.h" +#include "sweep_context.h" +#include "sweep.h" + +/** + * + * @author Mason Green + * + */ + +namespace p2t { + +class CDT +{ +public: + + /** + * Constructor - add polyline with non repeating points + * + * @param polyline + */ + CDT(std::vector polyline); + + /** + * Destructor - clean up memory + */ + ~CDT(); + + /** + * Add a hole + * + * @param polyline + */ + void AddHole(std::vector polyline); + + /** + * Add a steiner point + * + * @param point + */ + void AddPoint(Point* point); + + /** + * Triangulate - do this AFTER you've added the polyline, holes, and Steiner points + */ + void Triangulate(); + + /** + * Get CDT triangles + */ + std::vector GetTriangles(); + + /** + * Get triangle map + */ + std::list GetMap(); + + private: + + /** + * Internals + */ + + SweepContext* sweep_context_; + Sweep* sweep_; + +}; + +} + +#endif diff --git a/polygon/poly2tri/sweep/sweep.cc b/polygon/poly2tri/sweep/sweep.cc index 992f0e5160..258df5db08 100644 --- a/polygon/poly2tri/sweep/sweep.cc +++ b/polygon/poly2tri/sweep/sweep.cc @@ -1,803 +1,803 @@ -/* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * Neither the name of Poly2Tri nor the names of its contributors may be - * used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#include -#include "sweep.h" -#include "sweep_context.h" -#include "advancing_front.h" -#include "../common/utils.h" - -namespace p2t { - -// Triangulate simple polygon with holes -void Sweep::Triangulate(SweepContext& tcx) -{ - tcx.InitTriangulation(); - tcx.CreateAdvancingFront(nodes_); - // Sweep points; build mesh - SweepPoints(tcx); - // Clean up - FinalizationPolygon(tcx); -} - -void Sweep::SweepPoints(SweepContext& tcx) -{ - for (int i = 1; i < tcx.point_count(); i++) { - Point& point = *tcx.GetPoint(i); - Node* node = &PointEvent(tcx, point); - for (unsigned int i = 0; i < point.edge_list.size(); i++) { - EdgeEvent(tcx, point.edge_list[i], node); - } - } -} - -void Sweep::FinalizationPolygon(SweepContext& tcx) -{ - // Get an Internal triangle to start with - Triangle* t = tcx.front()->head()->next->triangle; - Point* p = tcx.front()->head()->next->point; - while (!t->GetConstrainedEdgeCW(*p)) { - t = t->NeighborCCW(*p); - } - - // Collect interior triangles constrained by edges - tcx.MeshClean(*t); -} - -Node& Sweep::PointEvent(SweepContext& tcx, Point& point) -{ - Node& node = tcx.LocateNode(point); - Node& new_node = NewFrontTriangle(tcx, point, node); - - // Only need to check +epsilon since point never have smaller - // x value than node due to how we fetch nodes from the front - if (point.x <= node.point->x + EPSILON) { - Fill(tcx, node); - } - - //tcx.AddNode(new_node); - - FillAdvancingFront(tcx, new_node); - return new_node; -} - -void Sweep::EdgeEvent(SweepContext& tcx, Edge* edge, Node* node) -{ - tcx.edge_event.constrained_edge = edge; - tcx.edge_event.right = (edge->p->x > edge->q->x); - - if (IsEdgeSideOfTriangle(*node->triangle, *edge->p, *edge->q)) { - return; - } - - // For now we will do all needed filling - // TODO: integrate with flip process might give some better performance - // but for now this avoid the issue with cases that needs both flips and fills - FillEdgeEvent(tcx, edge, node); - EdgeEvent(tcx, *edge->p, *edge->q, node->triangle, *edge->q); -} - -void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangle, Point& point) -{ - if (IsEdgeSideOfTriangle(*triangle, ep, eq)) { - return; - } - - Point* p1 = triangle->PointCCW(point); - Orientation o1 = Orient2d(eq, *p1, ep); - if (o1 == COLLINEAR) { - if( triangle->Contains(&eq, p1)) { - triangle->MarkConstrainedEdge(&eq, p1 ); - // We are modifying the constraint maybe it would be better to - // not change the given constraint and just keep a variable for the new constraint - tcx.edge_event.constrained_edge->q = p1; - triangle = &triangle->NeighborAcross(point); - EdgeEvent( tcx, ep, *p1, triangle, *p1 ); - } else { - std::runtime_error("EdgeEvent - collinear points not supported"); - assert(0); - } - return; - } - - Point* p2 = triangle->PointCW(point); - Orientation o2 = Orient2d(eq, *p2, ep); - if (o2 == COLLINEAR) { - if( triangle->Contains(&eq, p2)) { - triangle->MarkConstrainedEdge(&eq, p2 ); - // We are modifying the constraint maybe it would be better to - // not change the given constraint and just keep a variable for the new constraint - tcx.edge_event.constrained_edge->q = p2; - triangle = &triangle->NeighborAcross(point); - EdgeEvent( tcx, ep, *p2, triangle, *p2 ); - } else { - std::runtime_error("EdgeEvent - collinear points not supported"); - assert(0); - } - return; - } - - if (o1 == o2) { - // Need to decide if we are rotating CW or CCW to get to a triangle - // that will cross edge - if (o1 == CW) { - triangle = triangle->NeighborCCW(point); - } else{ - triangle = triangle->NeighborCW(point); - } - EdgeEvent(tcx, ep, eq, triangle, point); - } else { - // This triangle crosses constraint so lets flippin start! - FlipEdgeEvent(tcx, ep, eq, triangle, point); - } -} - -bool Sweep::IsEdgeSideOfTriangle(Triangle& triangle, Point& ep, Point& eq) -{ - int index = triangle.EdgeIndex(&ep, &eq); - - if (index != -1) { - triangle.MarkConstrainedEdge(index); - Triangle* t = triangle.GetNeighbor(index); - if (t) { - t->MarkConstrainedEdge(&ep, &eq); - } - return true; - } - return false; -} - -Node& Sweep::NewFrontTriangle(SweepContext& tcx, Point& point, Node& node) -{ - Triangle* triangle = new Triangle(point, *node.point, *node.next->point); - - triangle->MarkNeighbor(*node.triangle); - tcx.AddToMap(triangle); - - Node* new_node = new Node(point); - nodes_.push_back(new_node); - - new_node->next = node.next; - new_node->prev = &node; - node.next->prev = new_node; - node.next = new_node; - - if (!Legalize(tcx, *triangle)) { - tcx.MapTriangleToNodes(*triangle); - } - - return *new_node; -} - -void Sweep::Fill(SweepContext& tcx, Node& node) -{ - Triangle* triangle = new Triangle(*node.prev->point, *node.point, *node.next->point); - - // TODO: should copy the constrained_edge value from neighbor triangles - // for now constrained_edge values are copied during the legalize - triangle->MarkNeighbor(*node.prev->triangle); - triangle->MarkNeighbor(*node.triangle); - - tcx.AddToMap(triangle); - - // Update the advancing front - node.prev->next = node.next; - node.next->prev = node.prev; - - // If it was legalized the triangle has already been mapped - if (!Legalize(tcx, *triangle)) { - tcx.MapTriangleToNodes(*triangle); - } - -} - -void Sweep::FillAdvancingFront(SweepContext& tcx, Node& n) +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include "sweep.h" +#include "sweep_context.h" +#include "advancing_front.h" +#include "../common/utils.h" + +namespace p2t { + +// Triangulate simple polygon with holes +void Sweep::Triangulate(SweepContext& tcx) { - - // Fill right holes - Node* node = n.next; - - while (node->next) { - // if HoleAngle exceeds 90 degrees then break. - if (LargeHole_DontFill(node)) break; - Fill(tcx, *node); - node = node->next; - } - - // Fill left holes - node = n.prev; - - while (node->prev) { - // if HoleAngle exceeds 90 degrees then break. - if (LargeHole_DontFill(node)) break; - Fill(tcx, *node); - node = node->prev; - } - - // Fill right basins - if (n.next && n.next->next) { - double angle = BasinAngle(n); - if (angle < PI_3div4) { - FillBasin(tcx, n); - } - } -} - -// True if HoleAngle exceeds 90 degrees. -bool Sweep::LargeHole_DontFill(Node* node) { - - Node* nextNode = node->next; - Node* prevNode = node->prev; - if (!AngleExceeds90Degrees(node->point, nextNode->point, prevNode->point)) - return false; - - // Check additional points on front. - Node* next2Node = nextNode->next; - // "..Plus.." because only want angles on same side as point being added. - if ((next2Node != NULL) && !AngleExceedsPlus90DegreesOrIsNegative(node->point, next2Node->point, prevNode->point)) - return false; - - Node* prev2Node = prevNode->prev; - // "..Plus.." because only want angles on same side as point being added. - if ((prev2Node != NULL) && !AngleExceedsPlus90DegreesOrIsNegative(node->point, nextNode->point, prev2Node->point)) - return false; - - return true; -} - -bool Sweep::AngleExceeds90Degrees(Point* origin, Point* pa, Point* pb) { - double angle = Angle(*origin, *pa, *pb); - bool exceeds90Degrees = ((angle > PI_div2) || (angle < -PI_div2)); - return exceeds90Degrees; -} - -bool Sweep::AngleExceedsPlus90DegreesOrIsNegative(Point* origin, Point* pa, Point* pb) { - double angle = Angle(*origin, *pa, *pb); - bool exceedsPlus90DegreesOrIsNegative = (angle > PI_div2) || (angle < 0); - return exceedsPlus90DegreesOrIsNegative; -} - -double Sweep::Angle(Point& origin, Point& pa, Point& pb) { - /* Complex plane - * ab = cosA +i*sinA - * ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx) - * atan2(y,x) computes the principal value of the argument function - * applied to the complex number x+iy - * Where x = ax*bx + ay*by - * y = ax*by - ay*bx - */ - double px = origin.x; - double py = origin.y; - double ax = pa.x- px; - double ay = pa.y - py; - double bx = pb.x - px; - double by = pb.y - py; - double x = ax * by - ay * bx; - double y = ax * bx + ay * by; - double angle = atan2(x, y); - return angle; -} - -double Sweep::BasinAngle(Node& node) -{ - double ax = node.point->x - node.next->next->point->x; - double ay = node.point->y - node.next->next->point->y; - return atan2(ay, ax); -} - -double Sweep::HoleAngle(Node& node) -{ - /* Complex plane - * ab = cosA +i*sinA - * ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx) - * atan2(y,x) computes the principal value of the argument function - * applied to the complex number x+iy - * Where x = ax*bx + ay*by - * y = ax*by - ay*bx - */ - double ax = node.next->point->x - node.point->x; - double ay = node.next->point->y - node.point->y; - double bx = node.prev->point->x - node.point->x; - double by = node.prev->point->y - node.point->y; - return atan2(ax * by - ay * bx, ax * bx + ay * by); -} - -bool Sweep::Legalize(SweepContext& tcx, Triangle& t) -{ - // To legalize a triangle we start by finding if any of the three edges - // violate the Delaunay condition - for (int i = 0; i < 3; i++) { - if (t.delaunay_edge[i]) - continue; - - Triangle* ot = t.GetNeighbor(i); - - if (ot) { - Point* p = t.GetPoint(i); - Point* op = ot->OppositePoint(t, *p); - int oi = ot->Index(op); - - // If this is a Constrained Edge or a Delaunay Edge(only during recursive legalization) - // then we should not try to legalize - if (ot->constrained_edge[oi] || ot->delaunay_edge[oi]) { - t.constrained_edge[i] = ot->constrained_edge[oi]; - continue; - } - - bool inside = Incircle(*p, *t.PointCCW(*p), *t.PointCW(*p), *op); - - if (inside) { - // Lets mark this shared edge as Delaunay - t.delaunay_edge[i] = true; - ot->delaunay_edge[oi] = true; - - // Lets rotate shared edge one vertex CW to legalize it - RotateTrianglePair(t, *p, *ot, *op); - - // We now got one valid Delaunay Edge shared by two triangles - // This gives us 4 new edges to check for Delaunay - - // Make sure that triangle to node mapping is done only one time for a specific triangle - bool not_legalized = !Legalize(tcx, t); - if (not_legalized) { - tcx.MapTriangleToNodes(t); - } - - not_legalized = !Legalize(tcx, *ot); - if (not_legalized) - tcx.MapTriangleToNodes(*ot); - - // Reset the Delaunay edges, since they only are valid Delaunay edges - // until we add a new triangle or point. - // XXX: need to think about this. Can these edges be tried after we - // return to previous recursive level? - t.delaunay_edge[i] = false; - ot->delaunay_edge[oi] = false; - - // If triangle have been legalized no need to check the other edges since - // the recursive legalization will handles those so we can end here. - return true; - } - } - } - return false; -} - -bool Sweep::Incircle(Point& pa, Point& pb, Point& pc, Point& pd) -{ - double adx = pa.x - pd.x; - double ady = pa.y - pd.y; - double bdx = pb.x - pd.x; - double bdy = pb.y - pd.y; - - double adxbdy = adx * bdy; - double bdxady = bdx * ady; - double oabd = adxbdy - bdxady; - - if (oabd <= 0) - return false; - - double cdx = pc.x - pd.x; - double cdy = pc.y - pd.y; - - double cdxady = cdx * ady; - double adxcdy = adx * cdy; - double ocad = cdxady - adxcdy; - - if (ocad <= 0) - return false; - - double bdxcdy = bdx * cdy; - double cdxbdy = cdx * bdy; - - double alift = adx * adx + ady * ady; - double blift = bdx * bdx + bdy * bdy; - double clift = cdx * cdx + cdy * cdy; - - double det = alift * (bdxcdy - cdxbdy) + blift * ocad + clift * oabd; - - return det > 0; -} - -void Sweep::RotateTrianglePair(Triangle& t, Point& p, Triangle& ot, Point& op) -{ - Triangle* n1, *n2, *n3, *n4; - n1 = t.NeighborCCW(p); - n2 = t.NeighborCW(p); - n3 = ot.NeighborCCW(op); - n4 = ot.NeighborCW(op); - - bool ce1, ce2, ce3, ce4; - ce1 = t.GetConstrainedEdgeCCW(p); - ce2 = t.GetConstrainedEdgeCW(p); - ce3 = ot.GetConstrainedEdgeCCW(op); - ce4 = ot.GetConstrainedEdgeCW(op); - - bool de1, de2, de3, de4; - de1 = t.GetDelunayEdgeCCW(p); - de2 = t.GetDelunayEdgeCW(p); - de3 = ot.GetDelunayEdgeCCW(op); - de4 = ot.GetDelunayEdgeCW(op); - - t.Legalize(p, op); - ot.Legalize(op, p); - - // Remap delaunay_edge - ot.SetDelunayEdgeCCW(p, de1); - t.SetDelunayEdgeCW(p, de2); - t.SetDelunayEdgeCCW(op, de3); - ot.SetDelunayEdgeCW(op, de4); - - // Remap constrained_edge - ot.SetConstrainedEdgeCCW(p, ce1); - t.SetConstrainedEdgeCW(p, ce2); - t.SetConstrainedEdgeCCW(op, ce3); - ot.SetConstrainedEdgeCW(op, ce4); - - // Remap neighbors - // XXX: might optimize the markNeighbor by keeping track of - // what side should be assigned to what neighbor after the - // rotation. Now mark neighbor does lots of testing to find - // the right side. - t.ClearNeighbors(); - ot.ClearNeighbors(); - if (n1) ot.MarkNeighbor(*n1); - if (n2) t.MarkNeighbor(*n2); - if (n3) t.MarkNeighbor(*n3); - if (n4) ot.MarkNeighbor(*n4); - t.MarkNeighbor(ot); -} - -void Sweep::FillBasin(SweepContext& tcx, Node& node) -{ - if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) { - tcx.basin.left_node = node.next->next; - } else { - tcx.basin.left_node = node.next; - } - - // Find the bottom and right node - tcx.basin.bottom_node = tcx.basin.left_node; - while (tcx.basin.bottom_node->next - && tcx.basin.bottom_node->point->y >= tcx.basin.bottom_node->next->point->y) { - tcx.basin.bottom_node = tcx.basin.bottom_node->next; - } - if (tcx.basin.bottom_node == tcx.basin.left_node) { - // No valid basin - return; - } - - tcx.basin.right_node = tcx.basin.bottom_node; - while (tcx.basin.right_node->next - && tcx.basin.right_node->point->y < tcx.basin.right_node->next->point->y) { - tcx.basin.right_node = tcx.basin.right_node->next; - } - if (tcx.basin.right_node == tcx.basin.bottom_node) { - // No valid basins - return; - } - - tcx.basin.width = tcx.basin.right_node->point->x - tcx.basin.left_node->point->x; - tcx.basin.left_highest = tcx.basin.left_node->point->y > tcx.basin.right_node->point->y; - - FillBasinReq(tcx, tcx.basin.bottom_node); -} - -void Sweep::FillBasinReq(SweepContext& tcx, Node* node) -{ - // if shallow stop filling - if (IsShallow(tcx, *node)) { - return; - } - - Fill(tcx, *node); - - if (node->prev == tcx.basin.left_node && node->next == tcx.basin.right_node) { - return; - } else if (node->prev == tcx.basin.left_node) { - Orientation o = Orient2d(*node->point, *node->next->point, *node->next->next->point); - if (o == CW) { - return; - } - node = node->next; - } else if (node->next == tcx.basin.right_node) { - Orientation o = Orient2d(*node->point, *node->prev->point, *node->prev->prev->point); - if (o == CCW) { - return; - } - node = node->prev; - } else { - // Continue with the neighbor node with lowest Y value - if (node->prev->point->y < node->next->point->y) { - node = node->prev; - } else { - node = node->next; - } - } - - FillBasinReq(tcx, node); -} - -bool Sweep::IsShallow(SweepContext& tcx, Node& node) -{ - double height; - - if (tcx.basin.left_highest) { - height = tcx.basin.left_node->point->y - node.point->y; - } else { - height = tcx.basin.right_node->point->y - node.point->y; - } - - // if shallow stop filling - if (tcx.basin.width > height) { - return true; - } - return false; -} - -void Sweep::FillEdgeEvent(SweepContext& tcx, Edge* edge, Node* node) -{ - if (tcx.edge_event.right) { - FillRightAboveEdgeEvent(tcx, edge, node); - } else { - FillLeftAboveEdgeEvent(tcx, edge, node); - } -} - -void Sweep::FillRightAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node) -{ - while (node->next->point->x < edge->p->x) { - // Check if next node is below the edge - if (Orient2d(*edge->q, *node->next->point, *edge->p) == CCW) { - FillRightBelowEdgeEvent(tcx, edge, *node); - } else { - node = node->next; - } - } -} - -void Sweep::FillRightBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) -{ - if (node.point->x < edge->p->x) { - if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) { - // Concave - FillRightConcaveEdgeEvent(tcx, edge, node); - } else{ - // Convex - FillRightConvexEdgeEvent(tcx, edge, node); - // Retry this one - FillRightBelowEdgeEvent(tcx, edge, node); - } - } -} - -void Sweep::FillRightConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) -{ - Fill(tcx, *node.next); - if (node.next->point != edge->p) { - // Next above or below edge? - if (Orient2d(*edge->q, *node.next->point, *edge->p) == CCW) { - // Below - if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) { - // Next is concave - FillRightConcaveEdgeEvent(tcx, edge, node); - } else { - // Next is convex - } - } + tcx.InitTriangulation(); + tcx.CreateAdvancingFront(nodes_); + // Sweep points; build mesh + SweepPoints(tcx); + // Clean up + FinalizationPolygon(tcx); +} + +void Sweep::SweepPoints(SweepContext& tcx) +{ + for (int i = 1; i < tcx.point_count(); i++) { + Point& point = *tcx.GetPoint(i); + Node* node = &PointEvent(tcx, point); + for (unsigned int i = 0; i < point.edge_list.size(); i++) { + EdgeEvent(tcx, point.edge_list[i], node); + } + } +} + +void Sweep::FinalizationPolygon(SweepContext& tcx) +{ + // Get an Internal triangle to start with + Triangle* t = tcx.front()->head()->next->triangle; + Point* p = tcx.front()->head()->next->point; + while (!t->GetConstrainedEdgeCW(*p)) { + t = t->NeighborCCW(*p); + } + + // Collect interior triangles constrained by edges + tcx.MeshClean(*t); +} + +Node& Sweep::PointEvent(SweepContext& tcx, Point& point) +{ + Node& node = tcx.LocateNode(point); + Node& new_node = NewFrontTriangle(tcx, point, node); + + // Only need to check +epsilon since point never have smaller + // x value than node due to how we fetch nodes from the front + if (point.x <= node.point->x + EPSILON) { + Fill(tcx, node); + } + + //tcx.AddNode(new_node); + + FillAdvancingFront(tcx, new_node); + return new_node; +} + +void Sweep::EdgeEvent(SweepContext& tcx, Edge* edge, Node* node) +{ + tcx.edge_event.constrained_edge = edge; + tcx.edge_event.right = (edge->p->x > edge->q->x); + + if (IsEdgeSideOfTriangle(*node->triangle, *edge->p, *edge->q)) { + return; + } + + // For now we will do all needed filling + // TODO: integrate with flip process might give some better performance + // but for now this avoid the issue with cases that needs both flips and fills + FillEdgeEvent(tcx, edge, node); + EdgeEvent(tcx, *edge->p, *edge->q, node->triangle, *edge->q); +} + +void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangle, Point& point) +{ + if (IsEdgeSideOfTriangle(*triangle, ep, eq)) { + return; + } + + Point* p1 = triangle->PointCCW(point); + Orientation o1 = Orient2d(eq, *p1, ep); + if (o1 == COLLINEAR) { + if( triangle->Contains(&eq, p1)) { + triangle->MarkConstrainedEdge(&eq, p1 ); + // We are modifying the constraint maybe it would be better to + // not change the given constraint and just keep a variable for the new constraint + tcx.edge_event.constrained_edge->q = p1; + triangle = &triangle->NeighborAcross(point); + EdgeEvent( tcx, ep, *p1, triangle, *p1 ); + } else { + std::runtime_error("EdgeEvent - collinear points not supported"); + assert(0); + } + return; + } + + Point* p2 = triangle->PointCW(point); + Orientation o2 = Orient2d(eq, *p2, ep); + if (o2 == COLLINEAR) { + if( triangle->Contains(&eq, p2)) { + triangle->MarkConstrainedEdge(&eq, p2 ); + // We are modifying the constraint maybe it would be better to + // not change the given constraint and just keep a variable for the new constraint + tcx.edge_event.constrained_edge->q = p2; + triangle = &triangle->NeighborAcross(point); + EdgeEvent( tcx, ep, *p2, triangle, *p2 ); + } else { + std::runtime_error("EdgeEvent - collinear points not supported"); + assert(0); + } + return; + } + + if (o1 == o2) { + // Need to decide if we are rotating CW or CCW to get to a triangle + // that will cross edge + if (o1 == CW) { + triangle = triangle->NeighborCCW(point); + } else{ + triangle = triangle->NeighborCW(point); + } + EdgeEvent(tcx, ep, eq, triangle, point); + } else { + // This triangle crosses constraint so lets flippin start! + FlipEdgeEvent(tcx, ep, eq, triangle, point); + } +} + +bool Sweep::IsEdgeSideOfTriangle(Triangle& triangle, Point& ep, Point& eq) +{ + int index = triangle.EdgeIndex(&ep, &eq); + + if (index != -1) { + triangle.MarkConstrainedEdge(index); + Triangle* t = triangle.GetNeighbor(index); + if (t) { + t->MarkConstrainedEdge(&ep, &eq); + } + return true; + } + return false; +} + +Node& Sweep::NewFrontTriangle(SweepContext& tcx, Point& point, Node& node) +{ + Triangle* triangle = new Triangle(point, *node.point, *node.next->point); + + triangle->MarkNeighbor(*node.triangle); + tcx.AddToMap(triangle); + + Node* new_node = new Node(point); + nodes_.push_back(new_node); + + new_node->next = node.next; + new_node->prev = &node; + node.next->prev = new_node; + node.next = new_node; + + if (!Legalize(tcx, *triangle)) { + tcx.MapTriangleToNodes(*triangle); + } + + return *new_node; +} + +void Sweep::Fill(SweepContext& tcx, Node& node) +{ + Triangle* triangle = new Triangle(*node.prev->point, *node.point, *node.next->point); + + // TODO: should copy the constrained_edge value from neighbor triangles + // for now constrained_edge values are copied during the legalize + triangle->MarkNeighbor(*node.prev->triangle); + triangle->MarkNeighbor(*node.triangle); + + tcx.AddToMap(triangle); + + // Update the advancing front + node.prev->next = node.next; + node.next->prev = node.prev; + + // If it was legalized the triangle has already been mapped + if (!Legalize(tcx, *triangle)) { + tcx.MapTriangleToNodes(*triangle); + } + +} + +void Sweep::FillAdvancingFront(SweepContext& tcx, Node& n) +{ + + // Fill right holes + Node* node = n.next; + + while (node->next) { + // if HoleAngle exceeds 90 degrees then break. + if (LargeHole_DontFill(node)) break; + Fill(tcx, *node); + node = node->next; + } + + // Fill left holes + node = n.prev; + + while (node->prev) { + // if HoleAngle exceeds 90 degrees then break. + if (LargeHole_DontFill(node)) break; + Fill(tcx, *node); + node = node->prev; + } + + // Fill right basins + if (n.next && n.next->next) { + double angle = BasinAngle(n); + if (angle < PI_3div4) { + FillBasin(tcx, n); + } + } +} + +// True if HoleAngle exceeds 90 degrees. +bool Sweep::LargeHole_DontFill(Node* node) { + + Node* nextNode = node->next; + Node* prevNode = node->prev; + if (!AngleExceeds90Degrees(node->point, nextNode->point, prevNode->point)) + return false; + + // Check additional points on front. + Node* next2Node = nextNode->next; + // "..Plus.." because only want angles on same side as point being added. + if ((next2Node != NULL) && !AngleExceedsPlus90DegreesOrIsNegative(node->point, next2Node->point, prevNode->point)) + return false; + + Node* prev2Node = prevNode->prev; + // "..Plus.." because only want angles on same side as point being added. + if ((prev2Node != NULL) && !AngleExceedsPlus90DegreesOrIsNegative(node->point, nextNode->point, prev2Node->point)) + return false; + + return true; +} + +bool Sweep::AngleExceeds90Degrees(Point* origin, Point* pa, Point* pb) { + double angle = Angle(*origin, *pa, *pb); + bool exceeds90Degrees = ((angle > PI_div2) || (angle < -PI_div2)); + return exceeds90Degrees; +} + +bool Sweep::AngleExceedsPlus90DegreesOrIsNegative(Point* origin, Point* pa, Point* pb) { + double angle = Angle(*origin, *pa, *pb); + bool exceedsPlus90DegreesOrIsNegative = (angle > PI_div2) || (angle < 0); + return exceedsPlus90DegreesOrIsNegative; +} + +double Sweep::Angle(Point& origin, Point& pa, Point& pb) { + /* Complex plane + * ab = cosA +i*sinA + * ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx) + * atan2(y,x) computes the principal value of the argument function + * applied to the complex number x+iy + * Where x = ax*bx + ay*by + * y = ax*by - ay*bx + */ + double px = origin.x; + double py = origin.y; + double ax = pa.x- px; + double ay = pa.y - py; + double bx = pb.x - px; + double by = pb.y - py; + double x = ax * by - ay * bx; + double y = ax * bx + ay * by; + double angle = atan2(x, y); + return angle; +} + +double Sweep::BasinAngle(Node& node) +{ + double ax = node.point->x - node.next->next->point->x; + double ay = node.point->y - node.next->next->point->y; + return atan2(ay, ax); +} + +double Sweep::HoleAngle(Node& node) +{ + /* Complex plane + * ab = cosA +i*sinA + * ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx) + * atan2(y,x) computes the principal value of the argument function + * applied to the complex number x+iy + * Where x = ax*bx + ay*by + * y = ax*by - ay*bx + */ + double ax = node.next->point->x - node.point->x; + double ay = node.next->point->y - node.point->y; + double bx = node.prev->point->x - node.point->x; + double by = node.prev->point->y - node.point->y; + return atan2(ax * by - ay * bx, ax * bx + ay * by); +} + +bool Sweep::Legalize(SweepContext& tcx, Triangle& t) +{ + // To legalize a triangle we start by finding if any of the three edges + // violate the Delaunay condition + for (int i = 0; i < 3; i++) { + if (t.delaunay_edge[i]) + continue; + + Triangle* ot = t.GetNeighbor(i); + + if (ot) { + Point* p = t.GetPoint(i); + Point* op = ot->OppositePoint(t, *p); + int oi = ot->Index(op); + + // If this is a Constrained Edge or a Delaunay Edge(only during recursive legalization) + // then we should not try to legalize + if (ot->constrained_edge[oi] || ot->delaunay_edge[oi]) { + t.constrained_edge[i] = ot->constrained_edge[oi]; + continue; + } + + bool inside = Incircle(*p, *t.PointCCW(*p), *t.PointCW(*p), *op); + + if (inside) { + // Lets mark this shared edge as Delaunay + t.delaunay_edge[i] = true; + ot->delaunay_edge[oi] = true; + + // Lets rotate shared edge one vertex CW to legalize it + RotateTrianglePair(t, *p, *ot, *op); + + // We now got one valid Delaunay Edge shared by two triangles + // This gives us 4 new edges to check for Delaunay + + // Make sure that triangle to node mapping is done only one time for a specific triangle + bool not_legalized = !Legalize(tcx, t); + if (not_legalized) { + tcx.MapTriangleToNodes(t); + } + + not_legalized = !Legalize(tcx, *ot); + if (not_legalized) + tcx.MapTriangleToNodes(*ot); + + // Reset the Delaunay edges, since they only are valid Delaunay edges + // until we add a new triangle or point. + // XXX: need to think about this. Can these edges be tried after we + // return to previous recursive level? + t.delaunay_edge[i] = false; + ot->delaunay_edge[oi] = false; + + // If triangle have been legalized no need to check the other edges since + // the recursive legalization will handles those so we can end here. + return true; + } + } + } + return false; +} + +bool Sweep::Incircle(Point& pa, Point& pb, Point& pc, Point& pd) +{ + double adx = pa.x - pd.x; + double ady = pa.y - pd.y; + double bdx = pb.x - pd.x; + double bdy = pb.y - pd.y; + + double adxbdy = adx * bdy; + double bdxady = bdx * ady; + double oabd = adxbdy - bdxady; + + if (oabd <= 0) + return false; + + double cdx = pc.x - pd.x; + double cdy = pc.y - pd.y; + + double cdxady = cdx * ady; + double adxcdy = adx * cdy; + double ocad = cdxady - adxcdy; + + if (ocad <= 0) + return false; + + double bdxcdy = bdx * cdy; + double cdxbdy = cdx * bdy; + + double alift = adx * adx + ady * ady; + double blift = bdx * bdx + bdy * bdy; + double clift = cdx * cdx + cdy * cdy; + + double det = alift * (bdxcdy - cdxbdy) + blift * ocad + clift * oabd; + + return det > 0; +} + +void Sweep::RotateTrianglePair(Triangle& t, Point& p, Triangle& ot, Point& op) +{ + Triangle* n1, *n2, *n3, *n4; + n1 = t.NeighborCCW(p); + n2 = t.NeighborCW(p); + n3 = ot.NeighborCCW(op); + n4 = ot.NeighborCW(op); + + bool ce1, ce2, ce3, ce4; + ce1 = t.GetConstrainedEdgeCCW(p); + ce2 = t.GetConstrainedEdgeCW(p); + ce3 = ot.GetConstrainedEdgeCCW(op); + ce4 = ot.GetConstrainedEdgeCW(op); + + bool de1, de2, de3, de4; + de1 = t.GetDelunayEdgeCCW(p); + de2 = t.GetDelunayEdgeCW(p); + de3 = ot.GetDelunayEdgeCCW(op); + de4 = ot.GetDelunayEdgeCW(op); + + t.Legalize(p, op); + ot.Legalize(op, p); + + // Remap delaunay_edge + ot.SetDelunayEdgeCCW(p, de1); + t.SetDelunayEdgeCW(p, de2); + t.SetDelunayEdgeCCW(op, de3); + ot.SetDelunayEdgeCW(op, de4); + + // Remap constrained_edge + ot.SetConstrainedEdgeCCW(p, ce1); + t.SetConstrainedEdgeCW(p, ce2); + t.SetConstrainedEdgeCCW(op, ce3); + ot.SetConstrainedEdgeCW(op, ce4); + + // Remap neighbors + // XXX: might optimize the markNeighbor by keeping track of + // what side should be assigned to what neighbor after the + // rotation. Now mark neighbor does lots of testing to find + // the right side. + t.ClearNeighbors(); + ot.ClearNeighbors(); + if (n1) ot.MarkNeighbor(*n1); + if (n2) t.MarkNeighbor(*n2); + if (n3) t.MarkNeighbor(*n3); + if (n4) ot.MarkNeighbor(*n4); + t.MarkNeighbor(ot); +} + +void Sweep::FillBasin(SweepContext& tcx, Node& node) +{ + if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) { + tcx.basin.left_node = node.next->next; + } else { + tcx.basin.left_node = node.next; + } + + // Find the bottom and right node + tcx.basin.bottom_node = tcx.basin.left_node; + while (tcx.basin.bottom_node->next + && tcx.basin.bottom_node->point->y >= tcx.basin.bottom_node->next->point->y) { + tcx.basin.bottom_node = tcx.basin.bottom_node->next; + } + if (tcx.basin.bottom_node == tcx.basin.left_node) { + // No valid basin + return; + } + + tcx.basin.right_node = tcx.basin.bottom_node; + while (tcx.basin.right_node->next + && tcx.basin.right_node->point->y < tcx.basin.right_node->next->point->y) { + tcx.basin.right_node = tcx.basin.right_node->next; + } + if (tcx.basin.right_node == tcx.basin.bottom_node) { + // No valid basins + return; + } + + tcx.basin.width = tcx.basin.right_node->point->x - tcx.basin.left_node->point->x; + tcx.basin.left_highest = tcx.basin.left_node->point->y > tcx.basin.right_node->point->y; + + FillBasinReq(tcx, tcx.basin.bottom_node); +} + +void Sweep::FillBasinReq(SweepContext& tcx, Node* node) +{ + // if shallow stop filling + if (IsShallow(tcx, *node)) { + return; + } + + Fill(tcx, *node); + + if (node->prev == tcx.basin.left_node && node->next == tcx.basin.right_node) { + return; + } else if (node->prev == tcx.basin.left_node) { + Orientation o = Orient2d(*node->point, *node->next->point, *node->next->next->point); + if (o == CW) { + return; + } + node = node->next; + } else if (node->next == tcx.basin.right_node) { + Orientation o = Orient2d(*node->point, *node->prev->point, *node->prev->prev->point); + if (o == CCW) { + return; + } + node = node->prev; + } else { + // Continue with the neighbor node with lowest Y value + if (node->prev->point->y < node->next->point->y) { + node = node->prev; + } else { + node = node->next; + } + } + + FillBasinReq(tcx, node); +} + +bool Sweep::IsShallow(SweepContext& tcx, Node& node) +{ + double height; + + if (tcx.basin.left_highest) { + height = tcx.basin.left_node->point->y - node.point->y; + } else { + height = tcx.basin.right_node->point->y - node.point->y; + } + + // if shallow stop filling + if (tcx.basin.width > height) { + return true; + } + return false; +} + +void Sweep::FillEdgeEvent(SweepContext& tcx, Edge* edge, Node* node) +{ + if (tcx.edge_event.right) { + FillRightAboveEdgeEvent(tcx, edge, node); + } else { + FillLeftAboveEdgeEvent(tcx, edge, node); + } +} + +void Sweep::FillRightAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node) +{ + while (node->next->point->x < edge->p->x) { + // Check if next node is below the edge + if (Orient2d(*edge->q, *node->next->point, *edge->p) == CCW) { + FillRightBelowEdgeEvent(tcx, edge, *node); + } else { + node = node->next; + } + } +} + +void Sweep::FillRightBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) +{ + if (node.point->x < edge->p->x) { + if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) { + // Concave + FillRightConcaveEdgeEvent(tcx, edge, node); + } else{ + // Convex + FillRightConvexEdgeEvent(tcx, edge, node); + // Retry this one + FillRightBelowEdgeEvent(tcx, edge, node); + } + } +} + +void Sweep::FillRightConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) +{ + Fill(tcx, *node.next); + if (node.next->point != edge->p) { + // Next above or below edge? + if (Orient2d(*edge->q, *node.next->point, *edge->p) == CCW) { + // Below + if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) { + // Next is concave + FillRightConcaveEdgeEvent(tcx, edge, node); + } else { + // Next is convex + } + } + } + +} + +void Sweep::FillRightConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) +{ + // Next concave or convex? + if (Orient2d(*node.next->point, *node.next->next->point, *node.next->next->next->point) == CCW) { + // Concave + FillRightConcaveEdgeEvent(tcx, edge, *node.next); + } else{ + // Convex + // Next above or below edge? + if (Orient2d(*edge->q, *node.next->next->point, *edge->p) == CCW) { + // Below + FillRightConvexEdgeEvent(tcx, edge, *node.next); + } else{ + // Above + } + } +} + +void Sweep::FillLeftAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node) +{ + while (node->prev->point->x > edge->p->x) { + // Check if next node is below the edge + if (Orient2d(*edge->q, *node->prev->point, *edge->p) == CW) { + FillLeftBelowEdgeEvent(tcx, edge, *node); + } else { + node = node->prev; + } + } +} + +void Sweep::FillLeftBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) +{ + if (node.point->x > edge->p->x) { + if (Orient2d(*node.point, *node.prev->point, *node.prev->prev->point) == CW) { + // Concave + FillLeftConcaveEdgeEvent(tcx, edge, node); + } else { + // Convex + FillLeftConvexEdgeEvent(tcx, edge, node); + // Retry this one + FillLeftBelowEdgeEvent(tcx, edge, node); + } + } +} + +void Sweep::FillLeftConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) +{ + // Next concave or convex? + if (Orient2d(*node.prev->point, *node.prev->prev->point, *node.prev->prev->prev->point) == CW) { + // Concave + FillLeftConcaveEdgeEvent(tcx, edge, *node.prev); + } else{ + // Convex + // Next above or below edge? + if (Orient2d(*edge->q, *node.prev->prev->point, *edge->p) == CW) { + // Below + FillLeftConvexEdgeEvent(tcx, edge, *node.prev); + } else{ + // Above + } + } +} + +void Sweep::FillLeftConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) +{ + Fill(tcx, *node.prev); + if (node.prev->point != edge->p) { + // Next above or below edge? + if (Orient2d(*edge->q, *node.prev->point, *edge->p) == CW) { + // Below + if (Orient2d(*node.point, *node.prev->point, *node.prev->prev->point) == CW) { + // Next is concave + FillLeftConcaveEdgeEvent(tcx, edge, node); + } else{ + // Next is convex + } + } + } + +} + +void Sweep::FlipEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* t, Point& p) +{ + Triangle& ot = t->NeighborAcross(p); + Point& op = *ot.OppositePoint(*t, p); + + if (&ot == NULL) { + // If we want to integrate the fillEdgeEvent do it here + // With current implementation we should never get here + //throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle"); + assert(0); + } + + if (InScanArea(p, *t->PointCCW(p), *t->PointCW(p), op)) { + // Lets rotate shared edge one vertex CW + RotateTrianglePair(*t, p, ot, op); + tcx.MapTriangleToNodes(*t); + tcx.MapTriangleToNodes(ot); + + if (p == eq && op == ep) { + if (eq == *tcx.edge_event.constrained_edge->q && ep == *tcx.edge_event.constrained_edge->p) { + t->MarkConstrainedEdge(&ep, &eq); + ot.MarkConstrainedEdge(&ep, &eq); + Legalize(tcx, *t); + Legalize(tcx, ot); + } else { + // XXX: I think one of the triangles should be legalized here? + } + } else { + Orientation o = Orient2d(eq, op, ep); + t = &NextFlipTriangle(tcx, (int)o, *t, ot, p, op); + FlipEdgeEvent(tcx, ep, eq, t, p); + } + } else { + Point& newP = NextFlipPoint(ep, eq, ot, op); + FlipScanEdgeEvent(tcx, ep, eq, *t, ot, newP); + EdgeEvent(tcx, ep, eq, t, p); + } +} + +Triangle& Sweep::NextFlipTriangle(SweepContext& tcx, int o, Triangle& t, Triangle& ot, Point& p, Point& op) +{ + if (o == CCW) { + // ot is not crossing edge after flip + int edge_index = ot.EdgeIndex(&p, &op); + ot.delaunay_edge[edge_index] = true; + Legalize(tcx, ot); + ot.ClearDelunayEdges(); + return t; + } + + // t is not crossing edge after flip + int edge_index = t.EdgeIndex(&p, &op); + + t.delaunay_edge[edge_index] = true; + Legalize(tcx, t); + t.ClearDelunayEdges(); + return ot; +} + +Point& Sweep::NextFlipPoint(Point& ep, Point& eq, Triangle& ot, Point& op) +{ + Orientation o2d = Orient2d(eq, op, ep); + if (o2d == CW) { + // Right + return *ot.PointCCW(op); + } else if (o2d == CCW) { + // Left + return *ot.PointCW(op); + } else{ + //throw new RuntimeException("[Unsupported] Opposing point on constrained edge"); + assert(0); + } +} + +void Sweep::FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& flip_triangle, + Triangle& t, Point& p) +{ + Triangle& ot = t.NeighborAcross(p); + Point& op = *ot.OppositePoint(t, p); + + if (&t.NeighborAcross(p) == NULL) { + // If we want to integrate the fillEdgeEvent do it here + // With current implementation we should never get here + //throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle"); + assert(0); + } + + if (InScanArea(eq, *flip_triangle.PointCCW(eq), *flip_triangle.PointCW(eq), op)) { + // flip with new edge op->eq + FlipEdgeEvent(tcx, eq, op, &ot, op); + // TODO: Actually I just figured out that it should be possible to + // improve this by getting the next ot and op before the the above + // flip and continue the flipScanEdgeEvent here + // set new ot and op here and loop back to inScanArea test + // also need to set a new flip_triangle first + // Turns out at first glance that this is somewhat complicated + // so it will have to wait. + } else{ + Point& newP = NextFlipPoint(ep, eq, ot, op); + FlipScanEdgeEvent(tcx, ep, eq, flip_triangle, ot, newP); } - -} - -void Sweep::FillRightConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) -{ - // Next concave or convex? - if (Orient2d(*node.next->point, *node.next->next->point, *node.next->next->next->point) == CCW) { - // Concave - FillRightConcaveEdgeEvent(tcx, edge, *node.next); - } else{ - // Convex - // Next above or below edge? - if (Orient2d(*edge->q, *node.next->next->point, *edge->p) == CCW) { - // Below - FillRightConvexEdgeEvent(tcx, edge, *node.next); - } else{ - // Above - } - } -} - -void Sweep::FillLeftAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node) -{ - while (node->prev->point->x > edge->p->x) { - // Check if next node is below the edge - if (Orient2d(*edge->q, *node->prev->point, *edge->p) == CW) { - FillLeftBelowEdgeEvent(tcx, edge, *node); - } else { - node = node->prev; - } - } -} - -void Sweep::FillLeftBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) -{ - if (node.point->x > edge->p->x) { - if (Orient2d(*node.point, *node.prev->point, *node.prev->prev->point) == CW) { - // Concave - FillLeftConcaveEdgeEvent(tcx, edge, node); - } else { - // Convex - FillLeftConvexEdgeEvent(tcx, edge, node); - // Retry this one - FillLeftBelowEdgeEvent(tcx, edge, node); - } - } -} - -void Sweep::FillLeftConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) -{ - // Next concave or convex? - if (Orient2d(*node.prev->point, *node.prev->prev->point, *node.prev->prev->prev->point) == CW) { - // Concave - FillLeftConcaveEdgeEvent(tcx, edge, *node.prev); - } else{ - // Convex - // Next above or below edge? - if (Orient2d(*edge->q, *node.prev->prev->point, *edge->p) == CW) { - // Below - FillLeftConvexEdgeEvent(tcx, edge, *node.prev); - } else{ - // Above - } - } -} - -void Sweep::FillLeftConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) -{ - Fill(tcx, *node.prev); - if (node.prev->point != edge->p) { - // Next above or below edge? - if (Orient2d(*edge->q, *node.prev->point, *edge->p) == CW) { - // Below - if (Orient2d(*node.point, *node.prev->point, *node.prev->prev->point) == CW) { - // Next is concave - FillLeftConcaveEdgeEvent(tcx, edge, node); - } else{ - // Next is convex - } - } - } - -} - -void Sweep::FlipEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* t, Point& p) -{ - Triangle& ot = t->NeighborAcross(p); - Point& op = *ot.OppositePoint(*t, p); - - if (&ot == NULL) { - // If we want to integrate the fillEdgeEvent do it here - // With current implementation we should never get here - //throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle"); - assert(0); - } - - if (InScanArea(p, *t->PointCCW(p), *t->PointCW(p), op)) { - // Lets rotate shared edge one vertex CW - RotateTrianglePair(*t, p, ot, op); - tcx.MapTriangleToNodes(*t); - tcx.MapTriangleToNodes(ot); - - if (p == eq && op == ep) { - if (eq == *tcx.edge_event.constrained_edge->q && ep == *tcx.edge_event.constrained_edge->p) { - t->MarkConstrainedEdge(&ep, &eq); - ot.MarkConstrainedEdge(&ep, &eq); - Legalize(tcx, *t); - Legalize(tcx, ot); - } else { - // XXX: I think one of the triangles should be legalized here? - } - } else { - Orientation o = Orient2d(eq, op, ep); - t = &NextFlipTriangle(tcx, (int)o, *t, ot, p, op); - FlipEdgeEvent(tcx, ep, eq, t, p); - } - } else { - Point& newP = NextFlipPoint(ep, eq, ot, op); - FlipScanEdgeEvent(tcx, ep, eq, *t, ot, newP); - EdgeEvent(tcx, ep, eq, t, p); - } -} - -Triangle& Sweep::NextFlipTriangle(SweepContext& tcx, int o, Triangle& t, Triangle& ot, Point& p, Point& op) -{ - if (o == CCW) { - // ot is not crossing edge after flip - int edge_index = ot.EdgeIndex(&p, &op); - ot.delaunay_edge[edge_index] = true; - Legalize(tcx, ot); - ot.ClearDelunayEdges(); - return t; - } - - // t is not crossing edge after flip - int edge_index = t.EdgeIndex(&p, &op); - - t.delaunay_edge[edge_index] = true; - Legalize(tcx, t); - t.ClearDelunayEdges(); - return ot; -} - -Point& Sweep::NextFlipPoint(Point& ep, Point& eq, Triangle& ot, Point& op) -{ - Orientation o2d = Orient2d(eq, op, ep); - if (o2d == CW) { - // Right - return *ot.PointCCW(op); - } else if (o2d == CCW) { - // Left - return *ot.PointCW(op); - } else{ - //throw new RuntimeException("[Unsupported] Opposing point on constrained edge"); - assert(0); - } -} - -void Sweep::FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& flip_triangle, - Triangle& t, Point& p) -{ - Triangle& ot = t.NeighborAcross(p); - Point& op = *ot.OppositePoint(t, p); - - if (&t.NeighborAcross(p) == NULL) { - // If we want to integrate the fillEdgeEvent do it here - // With current implementation we should never get here - //throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle"); - assert(0); - } - - if (InScanArea(eq, *flip_triangle.PointCCW(eq), *flip_triangle.PointCW(eq), op)) { - // flip with new edge op->eq - FlipEdgeEvent(tcx, eq, op, &ot, op); - // TODO: Actually I just figured out that it should be possible to - // improve this by getting the next ot and op before the the above - // flip and continue the flipScanEdgeEvent here - // set new ot and op here and loop back to inScanArea test - // also need to set a new flip_triangle first - // Turns out at first glance that this is somewhat complicated - // so it will have to wait. - } else{ - Point& newP = NextFlipPoint(ep, eq, ot, op); - FlipScanEdgeEvent(tcx, ep, eq, flip_triangle, ot, newP); - } } Sweep::~Sweep() { @@ -807,7 +807,7 @@ Sweep::~Sweep() { delete nodes_[i]; } -} - -} - +} + +} + diff --git a/polygon/poly2tri/sweep/sweep_context.h b/polygon/poly2tri/sweep/sweep_context.h index 266408dc2e..1010c0e8a8 100644 --- a/polygon/poly2tri/sweep/sweep_context.h +++ b/polygon/poly2tri/sweep/sweep_context.h @@ -1,186 +1,186 @@ -/* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * Neither the name of Poly2Tri nor the names of its contributors may be - * used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef SWEEP_CONTEXT_H -#define SWEEP_CONTEXT_H - -#include -#include -#include - -namespace p2t { - -// Inital triangle factor, seed triangle will extend 30% of -// PointSet width to both left and right. -const double kAlpha = 0.3; - -struct Point; -class Triangle; -struct Node; -struct Edge; -class AdvancingFront; - -class SweepContext { -public: - -/// Constructor -SweepContext(std::vector polyline); -/// Destructor -~SweepContext(); - -void set_head(Point* p1); - -Point* head(); - -void set_tail(Point* p1); - -Point* tail(); - -int point_count(); - -Node& LocateNode(Point& point); - -void RemoveNode(Node* node); - -void CreateAdvancingFront(std::vector nodes); - -/// Try to map a node to all sides of this triangle that don't have a neighbor -void MapTriangleToNodes(Triangle& t); - -void AddToMap(Triangle* triangle); - -Point* GetPoint(const int& index); - -Point* GetPoints(); - -void RemoveFromMap(Triangle* triangle); - -void AddHole(std::vector polyline); - -void AddPoint(Point* point); - -AdvancingFront* front(); - -void MeshClean(Triangle& triangle); - -std::vector GetTriangles(); +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SWEEP_CONTEXT_H +#define SWEEP_CONTEXT_H + +#include +#include +#include + +namespace p2t { + +// Inital triangle factor, seed triangle will extend 30% of +// PointSet width to both left and right. +const double kAlpha = 0.3; + +struct Point; +class Triangle; +struct Node; +struct Edge; +class AdvancingFront; + +class SweepContext { +public: + +/// Constructor +SweepContext(std::vector polyline); +/// Destructor +~SweepContext(); + +void set_head(Point* p1); + +Point* head(); + +void set_tail(Point* p1); + +Point* tail(); + +int point_count(); + +Node& LocateNode(Point& point); + +void RemoveNode(Node* node); + +void CreateAdvancingFront(std::vector nodes); + +/// Try to map a node to all sides of this triangle that don't have a neighbor +void MapTriangleToNodes(Triangle& t); + +void AddToMap(Triangle* triangle); + +Point* GetPoint(const int& index); + +Point* GetPoints(); + +void RemoveFromMap(Triangle* triangle); + +void AddHole(std::vector polyline); + +void AddPoint(Point* point); + +AdvancingFront* front(); + +void MeshClean(Triangle& triangle); + +std::vector GetTriangles(); std::list GetMap(); - -std::vector edge_list; - -struct Basin { - Node* left_node; - Node* bottom_node; - Node* right_node; - double width; - bool left_highest; - - Basin() : left_node(NULL), bottom_node(NULL), right_node(NULL), width(0.0), left_highest(false) - { - } - - void Clear() - { - left_node = NULL; - bottom_node = NULL; - right_node = NULL; - width = 0.0; - left_highest = false; - } -}; - -struct EdgeEvent { - Edge* constrained_edge; - bool right; - - EdgeEvent() : constrained_edge(NULL), right(false) - { - } -}; - -Basin basin; -EdgeEvent edge_event; - -private: - -friend class Sweep; - -std::vector triangles_; -std::list map_; -std::vector points_; - -// Advancing front -AdvancingFront* front_; -// head point used with advancing front -Point* head_; -// tail point used with advancing front -Point* tail_; - -Node *af_head_, *af_middle_, *af_tail_; - -void InitTriangulation(); -void InitEdges(std::vector polyline); - -}; - -inline AdvancingFront* SweepContext::front() -{ - return front_; -} - -inline int SweepContext::point_count() -{ - return points_.size(); -} - -inline void SweepContext::set_head(Point* p1) -{ - head_ = p1; -} - -inline Point* SweepContext::head() -{ - return head_; -} - -inline void SweepContext::set_tail(Point* p1) -{ - tail_ = p1; -} - -inline Point* SweepContext::tail() -{ - return tail_; -} - -} - -#endif + +std::vector edge_list; + +struct Basin { + Node* left_node; + Node* bottom_node; + Node* right_node; + double width; + bool left_highest; + + Basin() : left_node(NULL), bottom_node(NULL), right_node(NULL), width(0.0), left_highest(false) + { + } + + void Clear() + { + left_node = NULL; + bottom_node = NULL; + right_node = NULL; + width = 0.0; + left_highest = false; + } +}; + +struct EdgeEvent { + Edge* constrained_edge; + bool right; + + EdgeEvent() : constrained_edge(NULL), right(false) + { + } +}; + +Basin basin; +EdgeEvent edge_event; + +private: + +friend class Sweep; + +std::vector triangles_; +std::list map_; +std::vector points_; + +// Advancing front +AdvancingFront* front_; +// head point used with advancing front +Point* head_; +// tail point used with advancing front +Point* tail_; + +Node *af_head_, *af_middle_, *af_tail_; + +void InitTriangulation(); +void InitEdges(std::vector polyline); + +}; + +inline AdvancingFront* SweepContext::front() +{ + return front_; +} + +inline int SweepContext::point_count() +{ + return points_.size(); +} + +inline void SweepContext::set_head(Point* p1) +{ + head_ = p1; +} + +inline Point* SweepContext::head() +{ + return head_; +} + +inline void SweepContext::set_tail(Point* p1) +{ + tail_ = p1; +} + +inline Point* SweepContext::tail() +{ + return tail_; +} + +} + +#endif