You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
388 lines
16 KiB
388 lines
16 KiB
/******************************************************************************/
|
|
/* T_CURVE.CPP: IMPLEMENTATION OF THE CCurveTool CLASS */
|
|
/* */
|
|
/* */
|
|
/******************************************************************************/
|
|
/* */
|
|
/* Methods in this file */
|
|
/* */
|
|
/* CCurve Tool Class Object */
|
|
/* CCurveTool::CCurveTool */
|
|
/* CCurveTool::~CCurveTool */
|
|
/* CCurveTool::AdjustBoundingRect */
|
|
/* CCurveTool::AddPoint */
|
|
/* CCurveTool::SetCurrentPoint */
|
|
/* CCurveTool::DrawCurve */
|
|
/* CCurveTool::AdjustPointsForConstraint */
|
|
/* CCurveTool::PreProcessPoints */
|
|
/* CCurveTool::Render */
|
|
/* CCurveTool::OnStartDrag */
|
|
/* CCurveTool::OnEndDrag */
|
|
/* CCurveTool::OnDrag */
|
|
/* CCurveTool::OnCancel */
|
|
/* CCurveTool::CanEndMultiptOperation */
|
|
/* CCurveTool::EndMultiptOperation */
|
|
/******************************************************************************/
|
|
/* */
|
|
/* Briefly, this Object draws a curve from 4 (currently) points. It generates */
|
|
/* a list of points which are placed in the array, and then calls polyline */
|
|
/* to draw line segments to build a curve. */
|
|
/* */
|
|
/* The array is divided into 2 pieces. The first piece is the anchor points, */
|
|
/* the 2nd piece is the array of points which will be passed to polyline. */
|
|
/* The anchor points are placed in the array in the following order */
|
|
/* 2,3,4,...1. See the addpoint method below for info on this order. */
|
|
/* */
|
|
/******************************************************************************/
|
|
#include "stdafx.h"
|
|
#include "global.h"
|
|
#include "pbrush.h"
|
|
#include "pbrusdoc.h"
|
|
#include "pbrusfrm.h"
|
|
#include "bmobject.h"
|
|
#include "imgsuprt.h"
|
|
#include "imgwnd.h"
|
|
#include "imgwell.h"
|
|
#include "pbrusvw.h"
|
|
#include "t_curve.h"
|
|
|
|
#ifdef _DEBUG
|
|
#undef THIS_FILE
|
|
static CHAR BASED_CODE THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
IMPLEMENT_DYNAMIC(CCurveTool, CRubberTool)
|
|
|
|
#include "memtrace.h"
|
|
|
|
extern CLineTool NEAR g_lineTool;
|
|
|
|
CCurveTool NEAR g_curveTool;
|
|
|
|
|
|
/******************************************************************************/
|
|
CCurveTool::CCurveTool()
|
|
{
|
|
m_nCmdID = IDMB_CURVETOOL;
|
|
m_iNumAnchorPoints = 0;
|
|
m_cRectBounding.SetRectEmpty();
|
|
}
|
|
|
|
/******************************************************************************/
|
|
CCurveTool::~CCurveTool()
|
|
{
|
|
m_cRectBounding.SetRectEmpty();
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/* recalculate the bounding rectangle for the polyline/curve */
|
|
void CCurveTool::AdjustBoundingRect(void)
|
|
{
|
|
int iStrokeWidth = GetStrokeWidth();
|
|
int i;
|
|
|
|
if (m_iNumAnchorPoints >= 1)
|
|
{
|
|
//set the rect to equal the 1st value
|
|
m_cRectBounding.SetRect(m_PolyPoints[0].x, m_PolyPoints[0].y,
|
|
m_PolyPoints[0].x, m_PolyPoints[0].y);
|
|
}
|
|
|
|
for (i=1; i < m_iNumAnchorPoints; i++)
|
|
{
|
|
m_cRectBounding.SetRect( min(m_PolyPoints[i].x, m_cRectBounding.left),
|
|
min(m_PolyPoints[i].y, m_cRectBounding.top),
|
|
max(m_PolyPoints[i].x, m_cRectBounding.right),
|
|
max(m_PolyPoints[i].y, m_cRectBounding.bottom));
|
|
}
|
|
|
|
// Adjust rectangle for Windows GDI (Non-inclusive right/bottom)
|
|
m_cRectBounding.bottom++; m_cRectBounding.right++;
|
|
|
|
// Adjust for width of current drawing line/border
|
|
m_cRectBounding.OffsetRect(-(iStrokeWidth/2),-(iStrokeWidth/2));
|
|
m_cRectBounding.InflateRect(iStrokeWidth, iStrokeWidth);
|
|
}
|
|
/******************************************************************************/
|
|
// This method adds a new point into the array and increases the number of
|
|
// anchor points currently in the array. If there are no points in the
|
|
// array, it adds a point to the 1st position (index 0). If there are any
|
|
// points currently in the array, it copies the last point to the new
|
|
// location, and then adds the new point where the old last point was (1 point
|
|
// before the last point. The 1st point added is always the last point in the
|
|
// array, and the 2nd point added is always the 1st point.
|
|
// The order of the points in the array are: 2,3,4,....,1
|
|
void CCurveTool::AddPoint(POINT ptNewPoint)
|
|
{
|
|
BOOL bRC = TRUE;
|
|
|
|
if (m_iNumAnchorPoints == 0)
|
|
{
|
|
m_PolyPoints[m_iNumAnchorPoints] = ptNewPoint;
|
|
m_iNumAnchorPoints++;
|
|
}
|
|
else
|
|
{
|
|
if (m_iNumAnchorPoints < MAX_ANCHOR_POINTS)
|
|
{
|
|
m_PolyPoints[m_iNumAnchorPoints] = m_PolyPoints[m_iNumAnchorPoints-1];
|
|
m_PolyPoints[m_iNumAnchorPoints-1] = ptNewPoint;
|
|
m_iNumAnchorPoints++;
|
|
}
|
|
}
|
|
|
|
|
|
AdjustBoundingRect();
|
|
}
|
|
/******************************************************************************/
|
|
// This method changes the value of the last point in the array. If there are
|
|
// 2 points, then it modifies the 2nd point, knowing that when there are only
|
|
// 2 points, we are draing a straight line (between 2 points). If there are
|
|
// more than 2 points, it modified the second to last point in the array,
|
|
// which is actually the last point dropped/placed. See above for expl of
|
|
// order of points in the array.
|
|
void CCurveTool::SetCurrentPoint(POINT ptNewPoint)
|
|
{
|
|
if (m_iNumAnchorPoints == 2)
|
|
{
|
|
m_PolyPoints[m_iNumAnchorPoints-1] = ptNewPoint;
|
|
}
|
|
else
|
|
{
|
|
if (m_iNumAnchorPoints > 2)
|
|
{
|
|
m_PolyPoints[m_iNumAnchorPoints-2] = ptNewPoint;
|
|
}
|
|
}
|
|
AdjustBoundingRect();
|
|
}
|
|
/******************************************************************************/
|
|
BOOL CCurveTool::DrawCurve(CDC* pDC)
|
|
{
|
|
POINT ptCurve[MAX_ANCHOR_POINTS];
|
|
UINT uPoints = m_iNumAnchorPoints;
|
|
int i;
|
|
|
|
for (i=uPoints-1; i>=0; --i)
|
|
{
|
|
ptCurve[i] = m_PolyPoints[i];
|
|
}
|
|
|
|
// HACK: PolyBezier cannot handle 3 points, so repeat the middle point
|
|
if (uPoints == 3)
|
|
{
|
|
ptCurve[3] = ptCurve[2];
|
|
ptCurve[2] = ptCurve[1];
|
|
uPoints = 4;
|
|
}
|
|
|
|
PolyBezier(pDC->m_hDC, ptCurve, uPoints);
|
|
|
|
return(TRUE);
|
|
}
|
|
/******************************************************************************/
|
|
/* Call the line's adjustpointsforconstraint member function */
|
|
/* only do this if there are 2 points (i.e. drawing a straight line */
|
|
void CCurveTool::AdjustPointsForConstraint(MTI *pmti)
|
|
{
|
|
if (m_iNumAnchorPoints == 2)
|
|
{
|
|
g_lineTool.AdjustPointsForConstraint(pmti);
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
// ptDown must be anchor point for our line, not where we did mouse button down
|
|
// on a subsequent point in the multipt operation
|
|
void CCurveTool::PreProcessPoints(MTI *pmti)
|
|
{
|
|
pmti->ptDown = m_PolyPoints[0];
|
|
CRubberTool::PreProcessPoints(pmti);
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/* Render sets up the pen and brush, and then calls either Render */
|
|
/* The pen and brush is set up exactly the same as the parent routine in */
|
|
/* CRubberTool. If there are only 2 points, do the standard line drawing */
|
|
/* using moveto and lineto, instead of trying to create a curve between 2 pts */
|
|
|
|
void CCurveTool::Render(CDC* pDC, CRect& rect, BOOL bDraw, BOOL bCommit, BOOL bCtrlDown)
|
|
{
|
|
// Setup Pen/Brush
|
|
SetupPenBrush( pDC->m_hDC, bDraw, TRUE, bCtrlDown );
|
|
|
|
if (m_iNumAnchorPoints == 2)
|
|
{
|
|
pDC->MoveTo( m_PolyPoints[0].x, m_PolyPoints[0].y );
|
|
pDC->LineTo( m_PolyPoints[1].x, m_PolyPoints[1].y );
|
|
}
|
|
else
|
|
{
|
|
if (m_iNumAnchorPoints > 2)
|
|
{
|
|
DrawCurve( pDC );
|
|
}
|
|
}
|
|
// Cleanup Pen/Brush
|
|
SetupPenBrush( pDC->m_hDC, bDraw, FALSE, bCtrlDown );
|
|
|
|
// Need to return the bounding rect
|
|
rect = m_cRectBounding;
|
|
}
|
|
|
|
void CCurveTool::OnActivate( BOOL bActivate )
|
|
{
|
|
if (!bActivate && m_bMultPtOpInProgress)
|
|
{
|
|
CImgWnd* pImgWnd = ((CPBView*)((CFrameWnd*)AfxGetMainWnd())->GetActiveView())->m_pImgWnd;
|
|
|
|
// Stolen from CPBView::OnEscape
|
|
// I don't think this can ever be NULL, but just in case
|
|
if (pImgWnd != NULL)
|
|
{
|
|
EndMultiptOperation( FALSE ); // end the multipt operation
|
|
|
|
Render(CDC::FromHandle(pImgWnd->m_pImg->hDC), m_cRectBounding, TRUE, TRUE, FALSE);
|
|
InvalImgRect(pImgWnd->m_pImg, &m_cRectBounding);
|
|
CommitImgRect(pImgWnd->m_pImg, &m_cRectBounding);
|
|
pImgWnd->FinishUndo(m_cRectBounding);
|
|
|
|
ClearStatusBarSize();
|
|
}
|
|
}
|
|
|
|
CRubberTool::OnActivate( bActivate );
|
|
}
|
|
|
|
|
|
/******************************************************************************/
|
|
/* On Start Drag is called on mouse button down. We basically call on Start */
|
|
/* Drag of the parent (default) class after adding in our point(s) into the */
|
|
/* array of points. If this is the first point (i.e. bMultiptOpInProgress == */
|
|
/* False, then we need 2 points in our array, and we can call the default */
|
|
/* OnStartDrag. If it is not the first point, then we just add the new point */
|
|
/* and call our OnDrag. In either case, OnDrag is called which eventually */
|
|
/* calls render to do our drawing on the mouse down */
|
|
/* We only call the parent OnStartDrag the first time, because it does some */
|
|
/* setup which we do not want done each time */
|
|
void CCurveTool::OnStartDrag( CImgWnd* pImgWnd, MTI* pmti )
|
|
{
|
|
if (m_bMultPtOpInProgress)
|
|
{
|
|
AddPoint(pmti->pt);
|
|
OnDrag(pImgWnd, pmti);
|
|
}
|
|
else
|
|
{
|
|
// must reset numAnchorPoints before calling addpoint the 1st time.
|
|
m_iNumAnchorPoints = 0;
|
|
AddPoint(pmti->pt);
|
|
m_bMultPtOpInProgress = TRUE;
|
|
// No Mult Pt In Progress => 1st Click
|
|
//
|
|
// add a 2nd point, last point is what we are draing to
|
|
// 1st point is anchor. 1st time, need 2 points to draw a line
|
|
// subsequent times, just add 1 point in array of points.
|
|
AddPoint(pmti->pt);
|
|
CRubberTool::OnStartDrag(pImgWnd, pmti);
|
|
}
|
|
|
|
}
|
|
/******************************************************************************/
|
|
/* On End Drag is sent on a mouse button up. This basically is a clone of the*/
|
|
/* CRubberTool::OnEndDrag method, except that we use our bounding rect for all*/
|
|
/* the image invalidation, and commit, and undo function calls. */
|
|
/* if we are in the middle of a multipoint operation, we do not want to call */
|
|
/* all the routines to fix the drawing (e.g. invalImgRect, CommitImgRect, */
|
|
/* FinishUndo). We just want to save the current point, render, and return */
|
|
void CCurveTool::OnEndDrag( CImgWnd* pImgWnd, MTI* pmti )
|
|
{
|
|
PreProcessPoints(pmti);
|
|
SetCurrentPoint(pmti->pt);
|
|
|
|
if (m_bMultPtOpInProgress)
|
|
{
|
|
// can't call OnDrag for this object/class, since it calls preprocesspt
|
|
// again, and then onDrag. If you call preproces again, you will lose
|
|
// bounding rectange box prev, and not be able to invalidate / repaint
|
|
// Still have to invalidate bounding rect, since if rect is larger than
|
|
// current rect, must invalidate to paint. E.g. If let off shift, then
|
|
// let off button, end point would be adjusted and bouning rect would
|
|
// also be correct, but rect calculated in CRubberTool::OnDrag is
|
|
// incorrect.
|
|
InvalImgRect(pImgWnd->m_pImg, &m_cRectBounding);
|
|
CRubberTool::OnDrag(pImgWnd, pmti);
|
|
}
|
|
else
|
|
{
|
|
OnDrag(pImgWnd, pmti); // one last time to refresh display in prep for final render
|
|
Render(CDC::FromHandle(pImgWnd->m_pImg->hDC), m_cRectBounding, pmti->fLeft, TRUE, pmti->fCtrlDown);
|
|
InvalImgRect(pImgWnd->m_pImg, &m_cRectBounding);
|
|
CommitImgRect(pImgWnd->m_pImg, &m_cRectBounding);
|
|
pImgWnd->FinishUndo(m_cRectBounding);
|
|
|
|
ClearStatusBarSize();
|
|
|
|
CImgTool::OnEndDrag(pImgWnd, pmti);
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/* On Drag is sent when the mouse is moved with the button down. We basically*/
|
|
/* save the current point, and call the base class processing. Since the base*/
|
|
/* class processing invalidates the rect on the screen and cleans it up so we */
|
|
/* can paint a new line, we have to adjust the previous rectangle to be the */
|
|
/* bounding rectangle of our polyline. If we did not do this, our previous */
|
|
/* drawing would not get erased, and we would be drawing our new line over */
|
|
/* part of the previous line. The default processing finally calls Render */
|
|
/* which since our render is virtual, will call our render method above. */
|
|
void CCurveTool::OnDrag( CImgWnd* pImgWnd, MTI* pmti )
|
|
{
|
|
PreProcessPoints(pmti);
|
|
SetCurrentPoint(pmti->pt);
|
|
CRubberTool::OnDrag(pImgWnd, pmti);
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/* On Cancel is sent when the user aborts an operation while in progress */
|
|
/* EndMultiptOperation with TRUE will do all our cleanup */
|
|
void CCurveTool::OnCancel(CImgWnd* pImgWnd)
|
|
{
|
|
EndMultiptOperation(TRUE);
|
|
CImgTool::OnCancel(pImgWnd);
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/* we can only end if the number of maximum points was entered. We must stay */
|
|
/* in capture/multiptmode until we get EXACTLY the desired number of anchor */
|
|
/* points */
|
|
BOOL CCurveTool::CanEndMultiptOperation(MTI* pmti )
|
|
{
|
|
|
|
if (m_iNumAnchorPoints == MAX_ANCHOR_POINTS)
|
|
{
|
|
m_bMultPtOpInProgress = FALSE;
|
|
}
|
|
else
|
|
{
|
|
m_bMultPtOpInProgress = TRUE;
|
|
}
|
|
|
|
return (CRubberTool::CanEndMultiptOperation(pmti));
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/* If bAbort is true, this means an error occurred, or the user cancelled the */
|
|
/* multipoint operation in the middle of it. We just set the num of anchor */
|
|
/* points to 0 to stop drawing and call the default endmultiptoperation */
|
|
void CCurveTool::EndMultiptOperation(BOOL bAbort)
|
|
{
|
|
if (bAbort)
|
|
{
|
|
m_iNumAnchorPoints = 0;
|
|
m_cRectBounding.SetRectEmpty();
|
|
}
|
|
|
|
CRubberTool::EndMultiptOperation();
|
|
}
|
|
|