#include "StdAfx.h" #include "itemcurvearc.h" #include "SigmaDoc.h" namespace NItem { static CRect CalculateHandleRect(LPPOINT pPoint); CItemCurveArc::CItemCurveArc(CSigmaDoc * ppDoc) : CItemCurve(ppDoc) , loop(0) { m_AddLineState = ppDoc->GetEditLineStatus(); m_pArc = new CItemArc(ppDoc); m_pArc->m_bDoSelectEnd = FALSE; } CItemCurveArc::~CItemCurveArc(void) { if (m_pArc)delete m_pArc; m_pArc = NULL; } BOOL CItemCurveArc::IsArcState(void) { if (m_AddLineState != CURVE_STATE_ARC) return FALSE; return TRUE; } void CItemCurveArc::OnLButtonDown(CDC *pDC, UINT nFlags, CPoint point, int vk) { if (!IsArcState()) CItemCurve::OnLButtonDown(pDC, nFlags, point, vk); else { ClearRedoList(); m_pArc->OnLButtonDown(pDC, nFlags, point, vk); if (loop == 2) { CArc* pArc = m_pArc->CreateArc(); if (pArc == NULL) return; CCurveEx* pCurve = pArc->GetCurve(); dfPoint pt; m_dpLastMousePos.x0 = m_pArc->PointStart.x0; m_dpLastMousePos.y0 = m_pArc->PointStart.y0; for (int i = 0; i < pCurve->num; i++) { pt.x0 = pCurve->x[i]; pt.y0 = pCurve->y[i]; DrawLineScreen(m_dpLastMousePos.x0, m_dpLastMousePos.y0, pt.x0, pt.y0); AddPoint(pt); m_dpLastMousePos = pt; } delete pArc; m_pArc->PointStart = m_pArc->PointEnd; m_pArc->PointMiddle = m_pArc->PointStart; m_pArc->loop = 1; loop = 0; } loop++; } } int CItemCurveArc::OnMouseMove(CDC* pDC, UINT nFlags, CPoint point)//, BYTE*& destBuffer, int& destLen) { if (!IsArcState()) { return CItemCurve::OnMouseMove(pDC, nFlags, point);//, destBuffer, destLen); } else if (m_pArc) { if (m_pArc->loop == 1) { if (!PointList.IsEmpty()) m_pArc->PointStart = PointList.GetTail(); } m_dpLastMousePos = GetRealPoint(PointList, nFlags, point); return m_pArc->OnMouseMove(pDC, nFlags, point); } return 0; } void CItemCurveArc::OnLButtonUp(CDC *pDC, UINT nFlags, CPoint point, int vk) { } void CItemCurveArc::SetCurveState(int state, CDC * pDC) { if (m_AddLineState == state) return; CItemCurve::SetCurveState(state, pDC); if (m_AddLineState == CURVE_STATE_ARC && PointList.GetCount() > 0) { m_pArc->PointStart = PointList.GetTail(); m_pArc->PointMiddle = m_pArc->PointStart; loop = 1; m_pArc->loop = loop; } } /////////////////////////////////////////////////////////////////////////////////////////////////// CItemCurveSpline::CItemCurveSpline(CSigmaDoc * ppDoc) : CItemCurveArc(ppDoc) { m_nSplineNodes = 5; m_mSmoothStepFactor = 3; } CItemCurveSpline::~CItemCurveSpline(void) { } void CItemCurveSpline::OnDraw(CXyDC* pDC) { } void CItemCurveSpline::DrawSplineCurve(CXyDC* pDC, const CPointList& controlPoints) { if (controlPoints.GetCount() <= 1) { return; } // Build spline curve from control points CPointList sampledPoints; BuildSplineCurve(controlPoints, sampledPoints); if (sampledPoints.GetCount() <= 1) { return; } // Draw the curve CDC* pdc = pDC->GetDC(); HDC hdc = pdc->m_hDC; CPen* pOld = pdc->SelectObject(&m_pen); POSITION pos = sampledPoints.GetHeadPosition(); dfPoint dp = sampledPoints.GetNext(pos); CPoint pt1 = pDC->GetScreen(dp.x0, dp.y0); CPoint pt2; POINT prePt; while (pos) { dp = sampledPoints.GetNext(pos); pt2 = pDC->GetScreen(dp.x0, dp.y0); ::MoveToEx(hdc, pt1.x, pt1.y, &prePt); ::LineTo(hdc, pt2.x, pt2.y); pt1 = pt2; } pdc->SelectObject(pOld); } void CItemCurveSpline::DrawControlPointHandles(CXyDC* pDC, const CPointList& controlPoints) { if (controlPoints.GetCount() == 0) { return; } CDC* pdc = pDC->GetDC(); CPen pen; pen.CreatePen(PS_SOLID, 2, RGB(0, 0, 255)); CPen* pOld = pdc->SelectObject(&pen); dfPoint prevPoint; BOOL hasPrev = FALSE; POSITION pos = controlPoints.GetHeadPosition(); while (pos) { dfPoint dp = controlPoints.GetNext(pos); // Skip duplicate points if (hasPrev && fabs(prevPoint.x0 - dp.x0) < 1e-3 && fabs(prevPoint.y0 - dp.y0) < 1e-3) continue; CPoint sp = pDC->GetScreen(dp.x0, dp.y0); CRect rt = CalculateHandleRect(&sp); rt.InflateRect(1, 1); pdc->Ellipse(&rt); prevPoint = dp; hasPrev = TRUE; } pdc->SelectObject(pOld); } void CItemCurveSpline::BuildSplineCurve(const CPointList& controlPoints, CPointList& outSampledPoints) { outSampledPoints.RemoveAll(); if (controlPoints.GetCount() <= 1) { return; } CCurveEx ce; ce.SetPoints(const_cast(controlPoints), 2); ce.CurveToSpline(outSampledPoints, GetDC()->GetMiniSmoothStep() * m_mSmoothStepFactor, 0); } size_t CItemCurveSpline::FindCommonPrefixLength(const std::vector& oldPoints, const std::vector& newPoints) { size_t commonCount = 0; size_t minSize = (oldPoints.size() < newPoints.size()) ? oldPoints.size() : newPoints.size(); for (size_t i = 0; i < minSize; i++) { if (oldPoints[i] == newPoints[i]) commonCount++; else break; } return commonCount; } void CItemCurveSpline::DrawPolylineXOR(CDC* pDC, const std::vector& points, size_t startIdx) { for (size_t i = startIdx; i + 1 < points.size(); i++) { pDC->MoveTo(points[i]); pDC->LineTo(points[i + 1]); } } void CItemCurveSpline::IncrementalUpdatePreview(CDC* pScreenDC, const std::vector& newScreenPoints) { size_t commonCount = FindCommonPrefixLength(m_cachedScreenPoints, newScreenPoints); // Setup XOR drawing mode int od = pScreenDC->SetROP2(R2_NOTXORPEN); CPen* pOld = pScreenDC->SelectObject(&m_pen); // Erase old different segments if (commonCount < m_cachedScreenPoints.size()) { // Start from commonCount-1 to ensure continuity, but don't go below 0 size_t startIdx = (commonCount > 0) ? commonCount - 1 : 0; DrawPolylineXOR(pScreenDC, m_cachedScreenPoints, startIdx); } // Draw new different segments if (commonCount < newScreenPoints.size()) { // Start from commonCount-1 to ensure continuity, but don't go below 0 size_t startIdx = (commonCount > 0) ? commonCount - 1 : 0; DrawPolylineXOR(pScreenDC, newScreenPoints, startIdx); } // Restore pScreenDC->SelectObject(pOld); pScreenDC->SetROP2(od); // Update cache m_cachedScreenPoints = newScreenPoints; } void CItemCurveSpline::ErasePreview(CDC* pScreenDC) { if (pScreenDC == 0 || m_cachedScreenPoints.size() <= 1) return; // Use XOR to erase using exact cached screen coordinates int od = pScreenDC->SetROP2(R2_NOTXORPEN); CPen* pOld = pScreenDC->SelectObject(&m_pen); for (size_t i = 0; i + 1 < m_cachedScreenPoints.size(); i++) { pScreenDC->MoveTo(m_cachedScreenPoints[i]); pScreenDC->LineTo(m_cachedScreenPoints[i + 1]); } pScreenDC->SelectObject(pOld); pScreenDC->SetROP2(od); m_cachedScreenPoints.clear(); } void CItemCurveSpline::DrawPreview(CDC* pScreenDC) { if (pScreenDC == 0 || m_controlPoints.GetCount() == 0) { return; } // Build preview control points (existing + current mouse position) CPointList previewControlPoints; POSITION pos = m_controlPoints.GetHeadPosition(); while (pos) { previewControlPoints.AddTail(m_controlPoints.GetNext(pos)); } previewControlPoints.AddTail(m_dpLastMousePos); if (previewControlPoints.GetCount() <= 1) { return; } // Build spline curve CPointList sampledPoints; BuildSplineCurve(previewControlPoints, sampledPoints); if (sampledPoints.GetCount() <= 1) { return; } // Convert to screen points CXyDC* pDC = GetDC(); if (pDC == 0) return; pDC->Create(pScreenDC); std::vector newScreenPoints; pos = sampledPoints.GetHeadPosition(); while (pos) { dfPoint dp = sampledPoints.GetNext(pos); CPoint pt = pDC->GetScreen(dp.x0, dp.y0); newScreenPoints.push_back(pt); } if (newScreenPoints.size() < 2) { return; } // Incremental update using XOR diff IncrementalUpdatePreview(pScreenDC, newScreenPoints); } void CItemCurveSpline::DrawControlPointHandles(CDC * pDC) { if (m_controlPoints.GetCount() > 0) { CXyDC* pXyDC = GetDC(); if (pXyDC) { pXyDC->Create(pDC); DrawControlPointHandles(pXyDC, m_controlPoints); } } } void CItemCurveSpline::OnLButtonDown(CDC *pDC, UINT nFlags, CPoint point, int vk) { if (!IsSplineState()) { CItemCurveArc::OnLButtonDown(pDC, nFlags, point, vk); return; } ClearRedoList(); dfPoint dp = GetRealPoint(m_controlPoints, nFlags, point); // Add control point m_controlPoints.AddTail(dp); TRACE("m_controlPoints.Count = %d\n", m_controlPoints.GetCount()); m_bStretchStart = TRUE; } int CItemCurveSpline::OnMouseMove(CDC* pDC, UINT nFlags, CPoint point) { m_pScreenDC = pDC; if (m_controlPoints.GetCount() == 0) { return 1; } // Update preview point dfPoint dp = GetRealPoint(m_controlPoints, nFlags, point); // Update mouse position m_dpLastMousePos = dp; // Draw new preview (with incremental diff update) DrawPreview(pDC); DrawControlPointHandles(pDC); m_bStretchStart = FALSE; return 1; } void CItemCurveSpline::OnLButtonUp(CDC *pDC, UINT nFlags, CPoint point, int vk) { if (!IsSplineState()) { CItemCurveArc::OnLButtonUp(pDC, nFlags, point, vk); } } void CItemCurveSpline::SetCurveState(int state, CDC * pDC) { if (state == m_AddLineState) { return; } if (m_AddLineState == CURVE_STATE_SPLINE) { AppendPoint(); // Finalize current spline } if (state == CURVE_STATE_SPLINE) { m_controlPoints.RemoveAll(); if (PointList.GetCount() > 0) { dfPoint dp = PointList.GetTail(); m_controlPoints.AddTail(dp); // Start new spline from last point if (!m_bStretchStart) { EraseRubberLine(); DrawPreview(pDC); m_bStretchStart = true; } } } CItemCurveArc::SetCurveState(state, pDC); } void CItemCurveSpline::AutoClose(void) { AppendPoint(); CItemCurveArc::AutoClose(); if (PointList.IsEmpty()) return; dfPoint dp = PointList.GetHead(); m_controlPoints.AddTail(dp); } POSITION CItemCurveSpline::NextCurve(void) { AppendPoint(); return CItemCurve::NextCurve(); } void CItemCurveSpline::AppendPoint(void) { if (!IsSplineState()) { return; } if (m_controlPoints.GetCount() <= 1) { return; } // Build complete spline curve including current mouse position CPointList finalControlPoints; POSITION pos = m_controlPoints.GetHeadPosition(); while (pos) { finalControlPoints.AddTail(m_controlPoints.GetNext(pos)); } finalControlPoints.AddTail(m_dpLastMousePos); // Convert to sampled points and add to finalized curve CPointList sampledPoints; BuildSplineCurve(finalControlPoints, sampledPoints); pos = sampledPoints.GetHeadPosition(); while (pos) { dfPoint dp = sampledPoints.GetNext(pos); AddPoint(dp); } clearSplineData(); } void CItemCurveSpline::clearSplineData() { m_controlPoints.RemoveAll(); m_cachedScreenPoints.clear(); } BOOL CItemCurveSpline::IsSplineState(void) { if (m_AddLineState == CURVE_STATE_SPLINE) return TRUE; return FALSE; } BOOL CItemCurveSpline::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { switch (nChar) { case VK_BACK: if (IsSplineState() && m_controlPoints.GetCount() > 0) { RemoveTail(); return FALSE; } break; case '1': //Line break; case '2': //Arc break; case '3': //Spline break; case '9': //90 degree break; } return CItemCurve::OnKeyDown(nChar, nRepCnt, nFlags); } BOOL CItemCurveSpline::IsOnlyPointListTail(void) { if (m_controlPoints.GetCount() == 1 && !PointList.IsEmpty()) { dfPoint ds = m_controlPoints.GetTail(); dfPoint dp = PointList.GetTail(); if (AfxGetPublicFunction()->Distance2(ds.x0, ds.y0, dp.x0, dp.y0) <= DBL_MIN) { return TRUE; } } return FALSE; } void CItemCurveSpline::OnCancel(void) { clearSplineData(); CItemCurve::OnCancel(); } void CItemCurveSpline::RemoveTail(void) { if (m_controlPoints.GetCount() == 0) return; // Normal case: multiple control points, incremental update handles erase+redraw if (m_controlPoints.GetCount() > 1) { m_controlPoints.RemoveTail(); DrawPreview(m_pScreenDC); DrawControlPointHandles(m_pScreenDC); return; } // Only one control point left: erase preview and fall back to PointList ErasePreview(m_pScreenDC); if (PointList.IsEmpty()) { clearSplineData(); return; } // Undo last finalized point PointList.RemoveTail(); if (PointList.IsEmpty()) { clearSplineData(); return; } // Restart spline from new tail of PointList m_controlPoints.RemoveAll(); m_controlPoints.AddTail(PointList.GetTail()); DrawPreview(m_pScreenDC); DrawControlPointHandles(m_pScreenDC); } void CItemCurveSpline::Undo(void) { if (m_controlPoints.IsEmpty()) { CItemCurveArc::Undo(); } else { RemoveTail(); } } void CItemCurveSpline::Redo(void) { if (IsOnlyPointListTail()) { CItemCurveArc::Redo(); m_controlPoints.RemoveAll(); if (!PointList.IsEmpty()) { dfPoint dp = PointList.GetTail(); m_controlPoints.AddTail(dp); } } } BOOL CItemCurveSpline::IsCanUndo(void) { if (IsOnlyPointListTail()) return CItemCurveArc::IsCanUndo(); return !m_controlPoints.IsEmpty(); } BOOL CItemCurveSpline::IsCanRedo(void) { if (IsOnlyPointListTail()) return CItemCurveArc::IsCanRedo(); return FALSE; } void CItemCurveSpline::EraseAuxiliarySpline(CDC * pScreenDC) { ErasePreview(pScreenDC); } void CItemCurveSpline::DrawAssistant(CDC * pScreenDC, int mouseX, int mouseY) { // 背景已重绘,清空缓存避免使用失效的屏幕坐标 m_cachedScreenPoints.clear(); DrawPreview(pScreenDC); DrawControlPointHandles(pScreenDC); } //////////////////////////////////////////////////////////////////////////////////////////////////////// //CItemCurveMerge CItemCurveMerge::CItemCurveMerge(CSigmaDoc * ppDoc) : CItemCurveSpline(ppDoc) { m_pItemSelect = new CItemSelectElement(ppDoc); m_pItemSelect->EnableDraw(FALSE); m_pItemSelect->SetCompareIdea(1); m_pItemSelect->SetNumber(-1); //CMainFrame* pmf = (CMainFrame*)GetDoc()->GetMainFrame(); //if(pmf)pmf->ShowToolBar(IDR_TOOLBAR_PLINE_INSERT, TRUE); } CItemCurveMerge::~CItemCurveMerge(void) { if (m_pItemSelect)delete m_pItemSelect; m_pItemSelect = NULL; //CMainFrame* pmf = (CMainFrame*)GetDoc()->GetMainFrame(); //if(pmf)pmf->ShowToolBar(IDR_TOOLBAR_PLINE_INSERT, FALSE); } BOOL CItemCurveMerge::IsMergeState(void) { if (m_AddLineState == CURVE_STATE_MERGE) return TRUE; return FALSE; } void CItemCurveMerge::OnLButtonDown(CDC *pDC, UINT nFlags, CPoint point, int vk) { GetDC()->Create(pDC); if (IsMergeState()) { m_pItemSelect->OnLButtonDown(pDC, nFlags, point, vk); if (m_pItemSelect->GetSelectCount() == 1) { EraseScreenLines(); CPoint2D dp = GetDC()->GetReal(point); POSITION pos = m_pItemSelect->GetSelectItem(0); MergeElement(pos, dp); m_pItemSelect->RemoveAllSelect(); //????????? RedrawScreenLines(); } } else CItemCurveSpline::OnLButtonDown(pDC, nFlags, point, vk); } int CItemCurveMerge::OnMouseMove(CDC *pDC, UINT nFlags, CPoint point)// , BYTE*& destBuffer, int& destLen) { if (IsMergeState() || m_AddLineState == CURVE_STATE_LINE || m_AddLineState == CURVE_STATE_ANGLE) { CItemCurve::OnMouseMove(pDC, nFlags, point); return 1; } SetScreenDC(pDC); GetDC()->Create(pDC); int count = m_ptDisposableList.GetSize() / 2; for (int i = 0; i < count; i++) { dfPoint pt1 = m_ptDisposableList.GetHead(); m_ptDisposableList.RemoveHead(); dfPoint pt2 = m_ptDisposableList.GetHead(); m_ptDisposableList.RemoveHead(); DrawLine(pt1, pt2); } m_ptDisposableList.RemoveAll(); CItemCurveSpline::OnMouseMove(pDC, nFlags, point);// , destBuffer, destLen); return 1; } void CItemCurveMerge::OnLButtonUp(CDC *pDC, UINT nFlags, CPoint point, int vk) { if (IsMergeState()) { m_pItemSelect->OnLButtonUp(pDC, nFlags, point, vk); } else CItemCurveSpline::OnLButtonUp(pDC, nFlags, point, vk); } void CItemCurveMerge::DrawAssistant(CDC * pScreenDC, int mouseX, int mouseY) { if (m_AddLineState == CURVE_STATE_SPLINE) { CItemCurveSpline::DrawAssistant(pScreenDC, mouseX, mouseY); } CItemCurve::DrawAssistant(pScreenDC, mouseX, mouseY); } BOOL CItemCurveMerge::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) { if (IsMergeState()) return m_pItemSelect->OnSetCursor(pWnd, nHitTest, message); return CItemCurveSpline::OnSetCursor(pWnd, nHitTest, message); } void CItemCurveMerge::EndCurve() { if (IsSplineState()) { CItemCurveSpline::NextCurve(); return; } else { CItemCurve::NextCurve(); } } void CItemCurveMerge::AddCurve() { NextCurve(); } void CItemCurveMerge::AutoClose(void) { AppendPoint(); if (PointList.IsEmpty()) return; dfPoint dp = PointList.GetHead(); m_ptDisposableList.AddTail(dp); //???????? m_ptDisposableList.AddTail(PointList.GetTail()); m_ptDisposableList.AddTail(m_dpLastMousePos); //????? m_ptDisposableList.AddTail(PointList.GetTail()); AddPoint(dp); m_dpLastMousePos = dp; } void CItemCurveMerge::ChangeMergeState() { if (m_AddLineState == CURVE_STATE_MERGE) { CItemCurve::RestoreCurveState(); if (PointList.IsEmpty()) return; dfPoint dp = PointList.GetTail(); m_controlPoints.AddTail(dp); } else { if (IsSplineState()) { AppendPoint(); } CItemCurve::SetCurveState(CURVE_STATE_MERGE, NULL); } } void CItemCurveMerge::OnCancel(void) { switch (m_AddLineState) { case CURVE_STATE_MERGE: case CURVE_STATE_LINE: case CURVE_STATE_ARC: case CURVE_STATE_ANGLE: CItemCurve::OnCancel(); break; case CURVE_STATE_SPLINE: CItemCurveSpline::OnCancel(); default: break; } } void CItemCurveMerge::SetCurveState(int state, CDC * pDC) { if (state == m_AddLineState)//???????? { return; } if (state == CURVE_STATE_MERGE) { m_AddLineState = CURVE_STATE_MERGE; CItemCurve::SetCurveState(CURVE_STATE_MERGE, NULL); return; } if (state == CURVE_STATE_DRAWING) { // No special handling needed } else { CItemCurveSpline::SetCurveState(state, pDC); } } void CItemCurveMerge::EreaseOldLine() { dfPoint ptTail = PointList.GetTail(); CPoint ptT = GetDC()->GetScreen(ptTail); CPoint ptPrev = GetDC()->GetScreen(m_dpLastMousePos); DrawLineScreen(ptPrev.x, ptPrev.y, ptT.x, ptT.y); } void CItemCurveMerge::DrawLine(dfPoint pt1, dfPoint pt2) { CPoint pt1S = GetDC()->GetScreen(pt1); CPoint pt2S = GetDC()->GetScreen(pt2); DrawLineScreen(pt1S.x, pt1S.y, pt2S.x, pt2S.y); } void CItemCurveMerge::MergeElement(POSITION pos, CPoint2D& dp) { if (pos == NULL) return; if (m_pDoc == NULL) return; COne* pOne = m_pDoc->GetDraw()->GetAt(pos); if (pOne == NULL) return; switch (pOne->GetType()) { case DOUBLEFOX_CURVE: { CCurveEx* pc = (CCurveEx*)pOne->GetValue(); double dis = AfxGetPublicFunction()->Distance2(dp.x0, dp.y0, pc->x[0], pc->y[0]); if (dis < AfxGetPublicFunction()->Distance2(dp.x0, dp.y0, pc->x[pc->num - 1], pc->y[pc->num - 1]))//???? { for (int i = 0; i < pc->num; i++) this->AddPointFromCoordinate(pc->x[i], pc->y[i]); } else//???? { int lastIndex = pc->num - 1; for (int i = lastIndex; i >= 0; i--) this->AddPointFromCoordinate(pc->x[i], pc->y[i]); } m_dpLastMousePos = PointList.GetTail(); } break; case DOUBLEFOX_MXN: case DOUBLEFOX_XYZ: case DOUBLEFOX_POINT: case DOUBLEFOX_TEXT: case DOUBLEFOX_ELLIPSE: case DOUBLEFOX_CIRCLE: case DOUBLEFOX_INSERT: case DOUBLEFOX_DRAW: case DOUBLEFOX_DRAW_RECT: case DOUBLEFOX_PROPORTION: case DOUBLEFOX_FRAME: case DOUBLEFOX_IMAGE: case DOUBLEFOX_OLE: case DOUBLEFOX_TREE: case DOUBLEFOX_WMF: case DOUBLEFOX_STATION: case DOUBLEFOX_MTEXT: { CPointNameBase* pPoint = (CPointNameBase*)pOne->GetValue(); dfPoint dp; dp.x0 = pPoint->x0; dp.y0 = pPoint->y0; AddPoint(dp); } break; } } static CRect CalculateHandleRect(LPPOINT pPoint) { CRect rc(*pPoint, *pPoint); //int d = m_HandleSize / 2; //m_HandleSize = 8 int d = 4; rc.InflateRect(d, d); return rc; } }//namespace