Mega Code Archive

 
Categories / Delphi / Examples
 

Picking

VARIOUS ROUTINES USED IN A GRAPHICS APP WRITTEN BY RDE: THEY ARE LIKELY TO REQUIRE ADAPTATION TO BE MADE TO WORK WITH WHATEVER (point and line) DATA STRUCTURES ARE PRESENT IN YOUR PARTICULAR APP -BUT THE ALGORITHMS ARE KOSHER Richard ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function TListsManager.GetNearestLineToClickPos(clickX, clickY: Integer): Integer; //CURRENTLY NOT CALLED {this is what's known in graphics programming circles as 'PICKING'...} {This function is generically useful although it is most appropriate in a situation where we have a three-layer heirarchy of data structures: 1) a 'display wireframe' [called here the displayMatrix] which is a projection of 3D co-ordinate data from the 'reference wireframe' [see 2 below] onto the 2D world of the screen 2) a 'reference wireframe' whose co-ordinate sets DON'T undergo all of the transformations that we might apply to the display wireframe. This reference wireframe [called ptMatrix here] is a stripped-down data set using pretty much exclusively COORDINATE data taken from the data structure on the bottom layer, 3) the 'Entity data' layer. See notes elsewhere for more detail, but suffice it to say here that one of the beauties of this arrangement is that we can ALSO use a 'wireFrameLines' array (where we keep track of INDEXES into the two point matrices [for line endpoints]) and whatever changes we make to our lines, in terms of length, angle, etc etc, we can still reference the same line entity on all three levels by virtue of the index which remains the same...} var slope : Real; XRealPrime : Real; YRealPrime : Real; dSquared : Real; endPt1Idx : Integer; endPt2Idx : Integer; X1 : Real; X2 : Real; Y1 : Real; Y2 : Real; lineIdx : Integer; distEndPt1Sqr: Real; distEndPt2Sqr: Real; lineDistSqr : Real; //not used anymore: we just select the NEAREST line instead... //insideSelRadius: Boolean; smallestDistSquared: Real; nearestLineIdx : Integer; begin nearestLineIdx := -1; smallestDistSquared := InitialValue; //UNcomment me //next line spurious //iterate through whatever LINES data //structure you have got to work with- //this code has been copied from //elsewhere and needs adapting... for lineIdx := 0 to 1 do //for lineIdx := 0 to (linesNumber - 1) do begin //UNcomment me //endPt1Idx := wFrameLines[lineIdx].endpt1; //endPt2Idx := wFrameLines[lineIdx].endpt2; //X1 := displayMatrix[endPt1Idx].x; //Y1 := displayMatrix[endPt1Idx].y; //X2 := displayMatrix[endPt2Idx].x; //Y2 := displayMatrix[endPt2Idx].y; lineDistSqr := (X2 - X1)*(X2 - X1)+(Y2 - Y1)*(Y2 - Y1); distEndPt1Sqr := (ClickX - X1)*(ClickX - X1)+(ClickY - Y1)*(ClickY - Y1); distEndPt2Sqr := (ClickX - X2)*(ClickX - X2)+(ClickY - Y2)*(ClickY - Y2); if (distEndPt1Sqr > lineDistSqr) or (distEndPt2Sqr > lineDistSqr) then begin continue; end; if ((X2 - X1) = 0) or ((Y2 - Y1) = 0) then begin if ((X2 - X1) = 0) then begin dSquared := (ClickX - X1) * (ClickX - X1); end else begin dSquared := (ClickY - Y1) * (ClickY - Y1); end; end else begin slope := (Y2 - Y1) / (X2 - X1); XRealPrime := (ClickY - Y1 + slope * X1 + ClickX / slope) / (slope + 1.0 / slope); YRealPrime := Y1 + slope * ((ClickY - Y1 + ClickX / slope - X1 / slope)/ (slope + 1.0 / slope)); dSquared := (ClickX - XRealPrime) * (ClickX - XRealPrime) + (ClickY - YRealPrime) * (ClickY - YRealPrime); end; (* //commented-out code here found the FIRST line only //whereas code below finds the VERY nearest //(without bothering with any sort of selection radius) if (dSquared <= selectionRadius) then begin insideSelRadius := True; {here we only bother with the first selectable entity that we find (rather than trying to catch ALL entity that are inside the clicking radius) but, if the first one we find is not the entity that the user intends to select 'it's tough': they'll have to find a better way of picking it (eg by rotating the viewpoint, etc etc)...} break; end; *) if dSquared < smallestDistSquared then begin smallestDistSquared := dSquared; nearestLineIdx := lineIdx; end; end; //end looping through all lines //UNcomment me //next line spurious if (nearestLineIdx > 1) then //if (nearestLineIdx > linesNumber) then begin Result := -1; end else Result := nearestLineIdx; end; /////////////////////////////////////////////////////////////////////////////////////////////// function TListsManager.GetPtToPtDistance2D(ptOne, ptTwo: TPoint3D): Real; //find the distance 'by the shortest route' between the two points //passed in. The formula used below is the 'basic'[!] formula known //to all[?] as Pythagoras' theorem (where you find the length of the //hypotenuse of the triangle that has the two points at each end of //the hypotenuse, and where the horizontal and vertical sides are //parallel to the x and y axes)... var distSquared: Real; begin distSquared := (((ptOne.x - ptTwo.x) * (ptOne.x - ptTwo.x)) + ((ptOne.y - ptTwo.y) * (ptOne.y - ptTwo.y))); Result := sqrt(distSquared); //IMPORTANT -if we use the z values, later, we must //code a 3D version of the same algorithm: //-simple: find the //sqr root of X squared plus Y squared plus Z squared... end; /////////////////////////////////////////////////////////////////// function TListsManager.GetNearestPtToClickPos(X, Y: Real): Integer; //CURRENTLY NOT CALLED //see also GetNearestLineToClickPos(). This is what's known in graphics //programming circles as 'PICKING'. Here we return the index of the Step POINT //nearest to the x,y coordinate passed in... //NOTE THAT WHILE THIS CODE (and a few subsequent not-called functions) MIGHT //BE USEFUL AT SOME POINT (since they implement a 'proper' 'picking' algorithm) //CURRENTLY WE DON'T NEED TO GO DOWN THAT ROAD. See IsScrPtInsideStep() and //GetStepUnderMouseIndex() below, where, when ascertaining which Step the user //has clicked on, we just check to see if the click is 'within the area //enclosed by the Step perimiter lines' (with a simple algorithm)... var thisDistSquared : Real; smallestDistSquared: Real; X1 : Real; Y1 : Real; ptIdx : Integer; ptsNumber : Integer; begin smallestDistSquared := InitialValue; Result := -1; //iterate through whatever POINTS data //structure you have got to work with- //this code has been copied from the //'Fox' project and needs adapting... ptsNumber := 69; //edit me for ptIdx := 0 to (ptsNumber) do begin //X1 := displayMatrix[ptIdx].x; //Y1 := displayMatrix[ptIdx].y; thisDistSquared := (X1 - X) * (X1 - X) + (Y1 - Y) * (Y1 - Y); if thisDistSquared < smallestDistSquared then begin smallestDistSquared := thisDistSquared; Result := PtIdx; end; end; end; ////////////////////////////////////////////////////////////////////////////////// function TListsManager.GetNearestLineEndToClickPos(LineIdx, XClick, YClick : Integer): Integer; //CURRENTLY NOT CALLED {return the number of the endpoint (1 or 2) of the line that is closest to the user mouse position. Note that here we use a simple algorithm that uses whichever is the smallest value, the x distance or the y distance from the click point to a line end, and then works with that, rather than doing a full 'Pythagoras' algorithm to find the distance...} var endPt1Idx : Integer; endPt2Idx : Integer; X1 : Integer; X2 : Integer; Y1 : Integer; Y2 : Integer; //distanceX1: Integer; //distanceY1: Integer; //distanceX2: Integer; //distanceY2: Integer; xDistToEnd1 : Integer; yDistToEnd1 : Integer; xDistToEnd2 : Integer; yDistToEnd2 : Integer; distToEnd1: Real; distToEnd2: Real; ptNum : Integer; begin ptNum := -1; if (lineIdx >= 0) then begin //UNcomment me //but SUBSTITUTE THE DATA STRUCTURES USED //IN this PROGRAM for displayMatrix etc etc //endPt1Idx := wFrameLines[lineIdx].endpt1; //endPt2Idx := wFrameLines[lineIdx].endpt2; //X1 := Round(displayMatrix[endPt1Idx].x); //Y1 := Round(displayMatrix[endPt1Idx].y); //X2 := Round(displayMatrix[endPt2Idx].x); //Y2 := Round(displayMatrix[endPt2Idx].y); if (XClick > X1) then xDistToEnd1 := (XClick - X1) else xDistToEnd1 := (X1 - XClick); if (YClick > Y1) then yDistToEnd1 := (YClick - Y1) else yDistToEnd1 := (Y1 - YClick); if (XClick > X2) then xDistToEnd2 := (XClick - X2) else xDistToEnd2 := (X2 - XClick); if (YClick > Y2) then yDistToEnd2 := (YClick - Y2) else yDistToEnd2 := (Y2 - YClick); distToEnd1 := sqrt((xDistToEnd1 * xDistToEnd1) + (yDistToEnd1 * yDistToEnd1)); distToEnd2 := sqrt((xDistToEnd2 * xDistToEnd2) + (yDistToEnd2 * yDistToEnd2)); if (distToEnd1 >= distToEnd2) then ptNum := 2; if (distToEnd2 >= distToEnd1) then ptNum := 1; end; Result := ptNum; end;