/***************************************************************************** * * Copyright (c) 1999, KL GROUP INC. All Rights Reserved. * http://www.klgroup.com * * This file is provided for demonstration and educational uses only. * Permission to use, copy, modify and distribute this file for * any purpose and without fee is hereby granted, provided that the * above copyright notice and this permission notice appear in all * copies, and that the name of KL Group not be used in advertising * or publicity pertaining to this material without the specific, * prior written permission of an authorized representative of * KL Group. * * KL GROUP MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY * OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, OR NON-INFRINGEMENT. KL GROUP SHALL NOT BE LIABLE FOR ANY * DAMAGES SUFFERED BY USERS AS A RESULT OF USING, MODIFYING OR * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. */ #include #include #include #include #include "olch2d.h" #include "olch3d.h" #include "profile.h" static BOOL bNewData; static BOOL bDragFlag; static BOOL bMoveLineFlag; static BOOL bStartPointOnGrid; static RECT rectProfileLine; static RECT rectIntersect; static char* pszHeaderStrings[3]; /* * Actions for profile definition * * These translations are hardcoded for this application. * Button1 is used to interactively define a line in the * xy-plane that defines a 2D profile. * * The user cannot start or end the profile line on a hole or * outside the grid. The user can however, start dragging on * a hole or a point outside the grid. As soon the mouse enters * the grid (with button 1 down) the profile definition begins. * If the mouse leaves the grid, profile definition is suspended * until the mouse enters it again. The profile then is defined * to be the last valid profile line before the user stops dragging. */ void DoProfile(SelectedPoint *spStart, SelectedPoint *spEnd, int nInterValue) { // A new profile has been defined -- Do the interpolation /* * Interpolate along the 3d-surface from ptStart to ptEnd * at n points. It uses Xrt3dZInterpolate to * do the interpolation. Send the new data points along with * header strings and line data styles to the 2d chart. Note, * that there could be holes in the 3d surface. Thus the * line will possibly be split up into different 2d data sets, * each with the same data syle. */ int i; double t, x, y, dx, dy, value; static char szHeaderStart[100], szHeaderEnd[100]; XrtDataStyle dataStyle; XrtData * pdata2d; XrtData *pdata2dOld; /* Update the header */ sprintf(szHeaderStart, "Start Point: (%.3lf,%.3lf)", spStart->xcoord, spStart->ycoord); sprintf(szHeaderEnd, "End Point: (%.3lf,%.3lf)", spEnd->xcoord, spEnd->ycoord); pszHeaderStrings[0] = szHeaderStart; pszHeaderStrings[1] = szHeaderEnd; pszHeaderStrings[2] = NULL; /* Create one data style for all the lines */ dataStyle.lpat = XRT_LPAT_SOLID; dataStyle.fpat = XRT_FPAT_SOLID; dataStyle.color = RGB(46, 139, 87); dataStyle.width = 3; dataStyle.point = XRT_POINT_CROSS; dataStyle.pcolor = RGB(25, 25, 112); dataStyle.psize = 7; /* Allocate new XrtData structure. Note, general data is a must here. Since, XrtMakeData makes all data sets the same size, explicitly allocate the structure. */ pdata2d = (XrtData *) calloc (1, sizeof(XrtData)); if (!pdata2d) { sprintf(szErrorBuf, "Cannot allocate XrtData in DoInterpolate"); MessageBox(NULL, szErrorBuf, NULL, MB_OK); return; } pdata2d->g.type = XRT_DATA_GENERAL; pdata2d->g.hole = XRT_HUGE_VAL; pdata2d->gen_nsets = 1; pdata2d->gen_data = (XrtGeneralData *) calloc (1, sizeof(XrtGeneralData)); if (!pdata2d->gen_data) { sprintf(szErrorBuf, "Cannot allocate XrtGeneralData in DoInterpolate"); MessageBox(NULL, szErrorBuf, NULL, MB_OK); return; } /* Transfer the points from arrays xpoints and ypoints to the XrtData structure. */ pdata2d->gen_npoints(0) = nInterValue; pdata2d->gen_xdata(0) = (double *) calloc (nInterValue, sizeof(double)); pdata2d->gen_ydata(0) = (double *) calloc (nInterValue, sizeof(double)); if (!(pdata2d->gen_xdata(0) && pdata2d->gen_ydata(0))) { sprintf(szErrorBuf, "Cannot allocate points in DoInterpolate"); MessageBox(NULL, szErrorBuf, NULL, MB_OK); return; } dx = spEnd->xcoord - spStart->xcoord; dy = spEnd->ycoord - spStart->ycoord; for (i = 0; i < nInterValue; i++) { /* The line is defined parametrically -- parameter t ranging from 0 to 1 */ t = (double) i / (double) (nInterValue - 1); x = spStart->xcoord + dx * t; y = spStart->ycoord + dy * t; pdata2d->gen_xel(0, i) = (double) t; value = Xrt3dZInterpolate(chart3d, x, y); pdata2d->gen_yel(0, i) = (value == dHoleValue) ? XRT_HUGE_VAL : (double) value; } /* Destroy old data set if around */ if (chart2d) { XrtGetValues(chart2d, XRT_DATA, &pdata2dOld, NULL); /* since we calloc'ed it, we must free it ourselves */ free(pdata2dOld->gen_ydata(0)); free(pdata2dOld->gen_xdata(0)); free(pdata2dOld->gen_data); free(pdata2dOld); } /* Create and/or update the 2d chart with the new data and header, and explicity set the data styles to be the same for each data set */ CreateProfileDialog(); XrtSetValues(chart2d, XRT_REPAINT, FALSE, NULL); XrtSetValues(chart2d, XRT_DATA, pdata2d, XRT_HEADER_STRINGS, pszHeaderStrings, NULL); XrtSetNthDataStyle(chart2d, 0, &dataStyle); XrtSetValues(chart2d, XRT_REPAINT, TRUE, NULL); } void DrawProfileLine(HXRT3D h3d, BOOL bErase, BOOL bClip) { /* * Draw the portion of the line which joins the start point to * the currently selected point (stored in the end point). This * is the code which provides the visual feedback for the * dragging and profiling. */ HDC hdc; HPEN hpen; int nDrawMode; if (bNewData) { return; } hdc = GetDC(gMainHwnd); SetMapMode(hdc, MM_TEXT); nDrawMode = GetROP2(hdc); SetROP2(hdc, R2_XORPEN); hpen = SelectObject(hdc, CreatePen(PS_SOLID, 2, RGB(250, 50, 0))); if (bClip) { IntersectClipRect(hdc, rectIntersect.left, rectIntersect.top+y3dOffset+1, rectIntersect.right, rectIntersect.bottom+y3dOffset+1); } MoveToEx(hdc, ptStart.xpixel, ptStart.ypixel+y3dOffset, NULL); LineTo(hdc, ptEnd.xpixel, ptEnd.ypixel+y3dOffset); DeleteObject(SelectObject(hdc, hpen)); SetROP2(hdc, nDrawMode); ReleaseDC(gMainHwnd, hdc); } double ConstrainValue(double value, double min, double max) { /* * Make sure value is between min and max. Used to constrain * x,y coordinates and the number of interoplation points. */ if (value >= max) { return (max); } if (value <= min) { return (min); } return(value); } void UpdateEditTexts(SelectedPoint *sp, double x, double y, BOOL bStart) { HANDLE hX; HANDLE hY; char szText[30]; if (bStart) { hX = hStartX; hY = hStartY; } else { hX = hEndX; hY = hEndY; } sprintf(szText, "%.3lf", x); SendMessage(hX, WM_SETTEXT, 0, (LPARAM) szText); sprintf(szText, "%.3lf", y); SendMessage(hY, WM_SETTEXT, 0, (LPARAM) szText); } void UpdatePixelPoint(SelectedPoint *sp) { /* * Set the pixel values of the selected point */ Xrt3dMapResult map; if (!bNewData) { Xrt3dUnmap(chart3d, sp->xcoord, sp->ycoord, sp->zvalue, &map); sp->xpixel = map.pix_x; sp->ypixel = map.pix_y; } else { sp->xpixel = 0; sp->ypixel = 0; } } void UpdateSelectedPoint(SelectedPoint *sp, double x, double y, double z, BOOL bStart) { /* * Set the value of the selected point to be (x,y,z) */ sp->xcoord = x; sp->ycoord = y; sp->zvalue = z; UpdatePixelPoint(sp); UpdateEditTexts(sp, x, y, bStart); } BOOL ProcessPoint(HXRT3D h3d, SelectedPoint *sp, int xpixel, int ypixel, BOOL bStart) { /* * Make sure the point on the screen represented by P = (xpixel, ypixel) * represents a point on the grid. The mapping routine tells us * exactly that. It returns XRT3D_HUGE_VAL (in Xrt3dMapResult) if * P is in a hole or outside of the grid. */ double x, y; Xrt3dMapResult map; /* At least one of mesh, shaded, contours, and zones must be true for there to be a visible chart */ if (nChart3dVisible && (Xrt3dMap(h3d, xpixel, ypixel, &map) == XRT3D_RGN_IN_GRAPH)) { x = ConstrainValue(map.x, sp->xmin, sp->xmax); y = ConstrainValue(map.y, sp->ymin, sp->ymax); if ((x == map.x) && (y == map.y) && (map.z != XRT3D_HUGE_VAL) && (map.z != dHoleValue)) { sp->xpixel = xpixel; sp->ypixel = ypixel; UpdateSelectedPoint(sp, x, y, map.z, bStart); return(TRUE); } } return(FALSE); } void StartDrag(HXRT3D h3d, int xPos, int yPos) { bDragFlag = TRUE; bStartPointOnGrid = FALSE; DrawProfileLine(h3d, TRUE, FALSE); if (ProcessPoint(h3d, &ptStart, xPos, yPos, TRUE)) { // start on the same start and end point bDragFlag = TRUE; ProcessPoint(h3d, &ptEnd, xPos, yPos, FALSE); bStartPointOnGrid = TRUE; } DrawProfileLine(h3d, FALSE, FALSE); } void ApplyDrag(HXRT3D h3d, int xPos, int yPos) { if (!bDragFlag) { return; } if (!bStartPointOnGrid) { // no start point yet StartDrag(h3d, xPos, yPos); return; } // drag the line DrawProfileLine(h3d, TRUE, FALSE); ProcessPoint(h3d, &ptEnd, xPos, yPos, FALSE); DrawProfileLine(h3d, FALSE, FALSE); DoProfile(&ptStart, &ptEnd, ctInterpolate.value); } void EndDrag(HXRT3D h3d, int xPos, int yPos) { // drag to the end point ApplyDrag(h3d, xPos, yPos); // no longer dragging bDragFlag = FALSE; bStartPointOnGrid = FALSE; } void ResetDrag() { /* * After a new input file has been loaded, start anew. */ bDragFlag = FALSE; bMoveLineFlag = FALSE; bStartPointOnGrid = FALSE; bNewData = TRUE; } void ApplyMoveLine(HXRT3D h3d, int xPos, int yPos) { int sx, sy, ex, ey; int dx, dy; if (!bMoveLineFlag) { return; } sx = ptStart.xpixel; sy = ptStart.ypixel; ex = ptEnd.xpixel; ey = ptEnd.ypixel; dx = (ex - sx) / 2; dy = (ey - sy) / 2; // move the line DrawProfileLine(h3d, TRUE, FALSE); if (!ProcessPoint(h3d, &ptStart, xPos-dx, yPos-dy, TRUE) || !ProcessPoint(h3d, &ptEnd, xPos+dx, yPos+dy, FALSE)) { ProcessPoint(h3d, &ptStart, sx, sy, TRUE); ProcessPoint(h3d, &ptEnd, ex, ey, FALSE); } DrawProfileLine(h3d, FALSE, FALSE); DoProfile(&ptStart, &ptEnd, ctInterpolate.value); } void StartMoveLine(HXRT3D h3d, int xPos, int yPos) { bMoveLineFlag = TRUE; ApplyMoveLine(h3d, xPos, yPos); } void EndMoveLine(HXRT3D h3d, int xPos, int yPos) { ApplyMoveLine(h3d, xPos, yPos); bMoveLineFlag = FALSE; } void ProcessInputData(Xrt3dData * pData3d) { /* * Set the min and max of the start and end points based on the * min and max of the 3d data. Initialize points to min values. */ double xmin, xmax; double ymin, ymax; double zmin; ResetDrag(); switch (pData3d->g.type) { case XRT3D_DATA_GRID: xmin = pData3d->g.xorig; xmax = pData3d->g.xorig + (pData3d->g.numx - 1) * pData3d->g.xstep; ymin = pData3d->g.yorig; ymax = pData3d->g.yorig + (pData3d->g.numy - 1) * pData3d->g.ystep; zmin = pData3d->g.values[0][0]; break; default: case XRT3D_DATA_IRGRID: xmin = pData3d->ig.xgrid[0]; xmax = pData3d->ig.xgrid[pData3d->ig.numx - 1]; ymin = pData3d->ig.ygrid[0]; ymax = pData3d->ig.ygrid[pData3d->ig.numy - 1]; zmin = pData3d->ig.values[0][0]; break; } ptStart.xmin = xmin; ptStart.xmax = xmax; ptEnd.xmin = xmin; ptEnd.xmax = xmax; ptStart.ymin = ymin; ptStart.ymax = ymax; ptEnd.ymin = ymin; ptEnd.ymax = ymax; UpdateSelectedPoint(&ptStart, xmin, ymin, zmin, TRUE); UpdateSelectedPoint(&ptEnd, xmin, ymin, zmin, FALSE); DoProfile(&ptStart, &ptEnd, ctInterpolate.value); bNewData = FALSE; } void ProcessEditPoint(SelectedPoint *sp, HANDLE hX, HANDLE hY, BOOL bStart) { double dXValue; double dYValue; double dInterValue; int nCount; char szText[30]; // get the point values nCount = (int) SendMessage(hX, WM_GETTEXT, 30, (LPARAM) szText); if (nCount == 0) { dXValue = sp->xmin; } else { dXValue = atof(szText); } nCount = (int) SendMessage(hY, WM_GETTEXT, 30, (LPARAM) szText); if (nCount == 0) { dYValue = sp->xmin; } else { dYValue = atof(szText); } // constrain it with the chart limit dYValue = ConstrainValue(dYValue, sp->xmin, sp->xmax); dInterValue = Xrt3dZInterpolate(chart3d, dXValue, dYValue); if (dInterValue == dHoleValue || dInterValue == XRT3D_HUGE_VAL) { // point can not be a hole value UpdateEditTexts(sp, sp->xcoord, sp->ycoord, bStart); } else { DrawProfileLine(chart3d, TRUE, FALSE); sp->xcoord = dXValue; sp->ycoord = dYValue; UpdateSelectedPoint(sp, dXValue, dYValue, dInterValue, bStart); DrawProfileLine(chart3d, FALSE, FALSE); } } int ProcessInterpolateValue(int nUpDown) { int nInterCount; int nCount; char szText[30]; // get the interpolation value nCount = (int) SendMessage(hInterpolate, WM_GETTEXT, 30, (LPARAM) szText); if (nCount == 0) { nInterCount = DEFAULT_NUM_INTERPOLATE; } else { nInterCount = atoi(szText); } // add the updown value nInterCount += nUpDown; if (nInterCount < ctInterpolate.min) { nInterCount = ctInterpolate.min; } else if (nInterCount > ctInterpolate.max) { nInterCount = ctInterpolate.max; } sprintf(szText, "%d", nInterCount); SendMessage(hInterpolate, WM_SETTEXT, 0, (LPARAM) szText); return nInterCount; } void ProcessEditValues() { ProcessEditPoint(&ptStart, hStartX, hStartY, TRUE); ProcessEditPoint(&ptEnd, hEndX, hEndY, FALSE); ctInterpolate.value = ProcessInterpolateValue(0); DoProfile(&ptStart, &ptEnd, ctInterpolate.value); } void SetProfileLineRect() { if (ptEnd.xpixel >= ptStart.xpixel) { rectProfileLine.left = ptStart.xpixel; rectProfileLine.right = ptEnd.xpixel; } else { rectProfileLine.left = ptEnd.xpixel; rectProfileLine.right = ptStart.xpixel; } if (ptEnd.ypixel >= ptStart.ypixel) { rectProfileLine.top = ptStart.ypixel; rectProfileLine.bottom = ptEnd.ypixel; } else { rectProfileLine.top = ptEnd.ypixel; rectProfileLine.bottom = ptStart.ypixel; } } void ReDrawProfileLine(LPRECT lpRectPainted) { // after a repaint/resize UpdatePixelPoint(&ptStart); UpdatePixelPoint(&ptEnd); SetProfileLineRect(); if (IntersectRect(&rectIntersect, &rectProfileLine, lpRectPainted)) { DrawProfileLine(chart3d, FALSE, TRUE); } }