<> <> <> <> <<>> DIRECTORY AtomButtonsTypes, CodeTimer, CoordSys, DisplayListToTree, Feedback, IO, Matrix3d, Preprocess3d, Real, RealFns, Rope, SV2d, SV3d, SVAssembly, SVCaret, SVCastRays, SVGravity, SVInterfaceTypes, SVLines3d, SVModelTypes, SVRay, SVScene, SVSceneTypes, SVState, SVVector3d; SVGravityImpl: CEDAR PROGRAM IMPORTS CodeTimer, CoordSys, DisplayListToTree, Feedback, Matrix3d, Preprocess3d, RealFns, SVAssembly, SVCaret, SVCastRays, SVLines3d, SVRay, SVState, SVVector3d EXPORTS SVGravity = BEGIN AlignBag: TYPE = SVInterfaceTypes.AlignBag; AlignmentLine: TYPE = SVInterfaceTypes.AlignmentLine; Camera: TYPE = SVSceneTypes.Camera; Classification: TYPE = SVSceneTypes.Classification; CSGTree: TYPE = SVSceneTypes.CSGTree; FeatureCycler: TYPE = REF FeatureCyclerObj; FeatureCyclerObj: TYPE = SVInterfaceTypes.FeatureCyclerObj; FeatureData: TYPE = SVInterfaceTypes.FeatureData; FeedbackData: TYPE = AtomButtonsTypes.FeedbackData; GoodPoint: TYPE = REF GoodPointObj; GoodPointObj: TYPE = SVInterfaceTypes.GoodPointObj; GravityType: TYPE = SVInterfaceTypes.GravityType; Line3d: TYPE = SV3d.Line3d; MasterObject: TYPE = SVSceneTypes.MasterObject; Matrix4by4: TYPE = SV3d.Matrix4by4; NearDistances: TYPE = REF NearDistancesObj; NearDistancesObj: TYPE = SVGravity.NearDistancesObj; NearFeatures: TYPE = REF NearFeaturesObj; NearFeaturesObj: TYPE = SVGravity.NearFeaturesObj; NearVertexEdgeAndFaces: TYPE = REF NearVertexEdgeAndFacesObj; NearVertexEdgeAndFacesObj: TYPE = SVInterfaceTypes.NearVertexEdgeAndFacesObj; Point2d: TYPE = SV2d.Point2d; Point3d: TYPE = SV3d.Point3d; PointAndDone: TYPE = SVSceneTypes.PointAndDone; PointGenerator: TYPE = SVSceneTypes.PointGenerator; Primitive: TYPE = SVSceneTypes.Primitive; Ray: TYPE = SVSceneTypes.Ray; Scene: TYPE = SVSceneTypes.Scene; SearchDepth: TYPE = SVSceneTypes.SearchDepth; Shape: TYPE = SVSceneTypes.Shape; Skitter: TYPE = SVSceneTypes.Skitter; Slice: TYPE = SVSceneTypes.Slice; SliceDescriptor: TYPE = SVSceneTypes.SliceDescriptor; TriggerBag: TYPE = SVInterfaceTypes.TriggerBag; Vector3d: TYPE = SV3d.Vector3d; SVData: TYPE = SVInterfaceTypes.SVData; Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = Feedback.Problem; maxFeatures: NAT _ 20; BestPoints: TYPE = REF BestPointsObj; BestPointsObj: TYPE = RECORD [ size: NAT, max, min: REAL, bestTossed: REAL, -- the distance of the closest object that has been thrown away dTol: REAL, -- min + s innerR: REAL, -- find all curves within this radius even if they are not neighbors of the nearest s: REAL, -- the size of neighborhoods. BestPoints should contain all objects that have been seen such that min <= dist(o, q) <= min+s, unless BestPoints overflows. overflow: BOOL, points: SEQUENCE len: NAT OF GoodPoint]; MultiGravityPool: TYPE = REF MultiGravityPoolObj; MultiGravityPoolObj: TYPE = RECORD [ distances: NearDistances, features: NearFeatures, bestpoints: BestPoints, bestcurves: BestPoints ]; EmptyBag: PROC [alignBag: AlignBag] RETURNS [BOOL] = { RETURN[ alignBag.slopeLines = NIL AND alignBag.slopePlanes = NIL AND alignBag.anchor = NIL]; }; EmptyTriggers: PROC [triggerBag: TriggerBag] RETURNS [BOOL] = { RETURN[ triggerBag.slices = NIL AND triggerBag.anchor = NIL ]; }; RayMap: PUBLIC PROC [cameraPt: Point2d, t: REAL, alignBag: AlignBag, sceneBag: TriggerBag, svData: SVData, intersections: BOOL _ FALSE] RETURNS [surfacePtWorld: Point3d, normalWorld: Vector3d, feature: FeatureData, hitData: REF ANY] = { gravityType: GravityType _ SVState.GetGravityType[svData]; camera: Camera _ svData.camera; CodeTimer.StartInt[$RayMap, $Solidviews]; IF SVState.GetGravity[svData] THEN { SELECT gravityType FROM facesPreferred => [surfacePtWorld, normalWorld, feature, hitData] _ FacesPreferred[cameraPt, t, alignBag, sceneBag, svData]; linesPreferred => [surfacePtWorld, normalWorld, feature, hitData] _ LinesPreferred[cameraPt, t, alignBag, sceneBag, svData, intersections]; pointsPreferred => [surfacePtWorld, normalWorld, feature, hitData] _ PointsPreferred[cameraPt, t, alignBag, sceneBag, svData, intersections]; ENDCASE => ERROR; } ELSE { parallel: BOOL; [surfacePtWorld, normalWorld, parallel] _ RayMeetsCentralCameraPlane[cameraPt, camera]; feature _ NIL; hitData _ NIL; }; CodeTimer.StopInt[$RayMap, $Solidviews]; }; RayMapCycler: PUBLIC PROC [cameraPt: Point2d, t: REAL, alignBag: AlignBag, sceneBag: TriggerBag, svData: SVData, intersections: BOOL _ FALSE] RETURNS [featureCycler: FeatureCycler] = { gravityType: GravityType _ SVState.GetGravityType[svData]; camera: Camera _ svData.camera; IF SVState.GetGravity[svData] THEN { SELECT gravityType FROM facesPreferred => featureCycler _ FacesPreferredCycler[cameraPt, t, alignBag, sceneBag, svData]; linesPreferred => featureCycler _ LinesPreferredCycler[cameraPt, t, alignBag, sceneBag, svData, intersections]; pointsPreferred => featureCycler _ PointsPreferredCycler[cameraPt, t, alignBag, sceneBag, svData, intersections]; ENDCASE => ERROR; } ELSE { featureCycler _ EmptyCycler[cameraPt, camera]; }; }; EmptyCycler: PROC [cameraPt: Point2d, camera: Camera] RETURNS [featureCycler: FeatureCycler] = { featureCycler _ NEW[FeatureCyclerObj _ [ nearVEF: NIL, count: 0, index: -1, cameraPt: cameraPt, camera: camera ]]; }; FacesPreferred: PUBLIC PROC [cameraPt: Point2d, t: REAL, alignBag: AlignBag, sceneBag: TriggerBag, svData: SVData] RETURNS [surfacePtWorld: Point3d, normalWorld: Vector3d, feature: FeatureData, hitData: REF ANY] = { <> nearVEF: NearVertexEdgeAndFaces; camera: Camera _ svData.camera; count: NAT; [nearVEF, count] _ MultiFacesPreferred[cameraPt, t, alignBag, sceneBag, svData]; IF count = 0 THEN { parallel: BOOL; [surfacePtWorld, normalWorld, parallel] _ RayMeetsCentralCameraPlane[cameraPt, camera]; hitData _ NIL; RETURN; }; <> RETURN PrepareWinner[nearVEF, 0]; }; LinesPreferred: PUBLIC PROC [cameraPt: Point2d, t: REAL, alignBag: AlignBag, sceneBag: TriggerBag, svData: SVData, intersections: BOOL] RETURNS [surfacePtWorld: Point3d, normalWorld: Vector3d, feature: FeatureData, hitData: REF ANY] = { <> <> <> nearVEF: NearVertexEdgeAndFaces; count: NAT; camera: Camera _ svData.camera; [nearVEF, count] _ MultiLinesPreferred[cameraPt, t, alignBag, sceneBag, svData, intersections]; IF count = 0 THEN { parallel: BOOL; [surfacePtWorld, normalWorld, parallel] _ RayMeetsCentralCameraPlane[cameraPt, camera]; feature _ NIL; hitData _ NIL; RETURN; }; IF count = 1 THEN RETURN PrepareWinner[nearVEF, 0] ELSE { <> nearestDist: REAL _ -1; bestSceneObject: INT _ -1; neighborCount: NAT _ 1; s: REAL = 0.072; -- 1/1000 inches nearestDist _ nearVEF[0].dist; <> FOR i: NAT IN [1..count) DO IF nearVEF[i].dist - nearestDist < s THEN neighborCount _ neighborCount + 1; ENDLOOP; IF neighborCount = 1 THEN RETURN PrepareWinner[nearVEF, 0]; bestSceneObject _ -1; RETURN PrepareWinner[nearVEF, 0]; }; }; PointsPreferred: PUBLIC PROC [cameraPt: Point2d, t: REAL, alignBag: AlignBag, sceneBag: TriggerBag, svData: SVData, intersections: BOOL] RETURNS [surfacePtWorld: Point3d, normalWorld: Vector3d, feature: FeatureData, hitData: REF ANY] = { count: NAT; nearVEF: NearVertexEdgeAndFaces; [nearVEF, count] _ MultiPointsPreferred[cameraPt, t, alignBag, sceneBag, svData, intersections]; IF count = 0 THEN { parallel: BOOL; camera: Camera _ svData.camera; [surfacePtWorld, normalWorld, parallel] _ RayMeetsCentralCameraPlane[cameraPt, camera]; feature _ NIL; hitData _ NIL; RETURN; }; IF count = 1 THEN RETURN PrepareWinner[nearVEF, 0] ELSE { <> neighborCount: NAT _ 1; s: REAL = 0.072; -- 1/1000 inches nearestDist: REAL _ -1; nearestDist _ nearVEF[0].dist; FOR i: NAT IN [1..count) DO IF nearVEF[i].dist - nearestDist < s THEN neighborCount _ neighborCount + 1; ENDLOOP; IF neighborCount = 1 THEN RETURN PrepareWinner[nearVEF, 0]; FOR i: NAT IN [0..neighborCount) DO IF nearVEF[i].featureData.type = slice THEN { RETURN PrepareWinner[nearVEF, i]; }; REPEAT FINISHED => RETURN PrepareWinner[nearVEF, 0]; ENDLOOP; }; }; PrepareWinner: PROC [nearVEF: NearVertexEdgeAndFaces, index: NAT] RETURNS [surfacePtWorld: Point3d, normalWorld: Vector3d, feature: FeatureData, hitData: REF ANY] = { goodPoint: GoodPoint _ nearVEF[index]; surfacePtWorld _ goodPoint.point; normalWorld _ goodPoint.normal; feature _ goodPoint.featureData; hitData _ goodPoint.hitData; }; FacesPreferredCycler: PUBLIC PROC [cameraPt: Point2d, t: REAL, alignBag: AlignBag, sceneBag: TriggerBag, svData: SVData] RETURNS [featureCycler: FeatureCycler] = { <> nearVEF: NearVertexEdgeAndFaces; camera: Camera _ svData.camera; count: NAT; BEGIN [nearVEF, count] _ MultiFacesPreferred[cameraPt, t, alignBag, sceneBag, svData]; IF count = 0 THEN RETURN[EmptyCycler[cameraPt, camera]]; GOTO MakeSimpleCycler; <> EXITS MakeSimpleCycler => { featureCycler _ NEW[FeatureCyclerObj _ [ nearVEF: nearVEF, count: count, index: 0, cameraPt: cameraPt, camera: camera ]]; }; END; }; LinesPreferredCycler: PUBLIC PROC [cameraPt: Point2d, t: REAL, alignBag: AlignBag, sceneBag: TriggerBag, svData: SVData, intersections: BOOL, maxDimension: [0..2] _ 2] RETURNS [featureCycler: FeatureCycler] = { <> <> <> nearVEF: NearVertexEdgeAndFaces; count: NAT; camera: Camera _ svData.camera; BEGIN [nearVEF, count] _ MultiLinesPreferred[cameraPt, t, alignBag, sceneBag, svData, intersections, maxDimension]; IF count = 0 THEN RETURN[EmptyCycler[cameraPt, camera]]; IF count = 1 THEN GOTO MakeSimpleCycler ELSE { <> nearestDist: REAL _ -1; bestSceneObject: INT _ -1; neighborCount: NAT _ 1; s: REAL = 0.072; -- 1/1000 inches nearestDist _ nearVEF[0].dist; <> FOR i: NAT IN [1..count) DO IF nearVEF[i].dist - nearestDist < s THEN neighborCount _ neighborCount + 1; ENDLOOP; IF neighborCount = 1 THEN GOTO MakeSimpleCycler; bestSceneObject _ -1; featureCycler _ NEW[FeatureCyclerObj _ [ nearVEF: nearVEF, count: count, index: 0, cameraPt: cameraPt, camera: camera ]]; }; EXITS MakeSimpleCycler => { featureCycler _ NEW[FeatureCyclerObj _ [ nearVEF: nearVEF, count: count, index: 0, cameraPt: cameraPt, camera: camera ]]; }; END; }; PointsPreferredCycler: PUBLIC PROC [cameraPt: Point2d, t: REAL, alignBag: AlignBag, sceneBag: TriggerBag, svData: SVData, intersections: BOOL, maxDimension: [0..2] _ 2] RETURNS [featureCycler: FeatureCycler] = { count: NAT; nearVEF: NearVertexEdgeAndFaces; camera: Camera _ svData.camera; BEGIN [nearVEF, count] _ MultiPointsPreferred[cameraPt, t, alignBag, sceneBag, svData, intersections, maxDimension]; IF count = 0 THEN RETURN[EmptyCycler[cameraPt, camera]]; IF count = 1 THEN GOTO MakeSimpleCycler ELSE { <> neighborCount: NAT _ 1; s: REAL = 0.072; -- 1/1000 inches nearestDist: REAL _ -1; featureCycler _ NEW[FeatureCyclerObj _ [ nearVEF: nearVEF, count: count, index: -1,-- will be filled in below cameraPt: cameraPt, camera: camera ]]; nearestDist _ nearVEF[0].dist; FOR i: NAT IN [1..count) DO IF nearVEF[i].dist - nearestDist < s THEN neighborCount _ neighborCount + 1; ENDLOOP; IF neighborCount = 1 THEN GOTO MakeSimpleCycler; FOR i: NAT IN [0..neighborCount) DO IF nearVEF[i].featureData.type = slice THEN { featureCycler.index _ i; RETURN; }; REPEAT FINISHED => featureCycler.index _ 0; ENDLOOP; }; EXITS MakeSimpleCycler => { featureCycler _ NEW[FeatureCyclerObj _ [ nearVEF: nearVEF, count: count, index: 0, cameraPt: cameraPt, camera: camera ]]; }; END; }; FirstFeature: PUBLIC PROC [featureCycler: FeatureCycler] RETURNS [surfacePtWorld: Point3d, normalWorld: Vector3d, feature: FeatureData, hitData: REF ANY] = { RETURN GetFeature[featureCycler, 0]; }; NextFeature: PUBLIC PROC [featureCycler: FeatureCycler] RETURNS [surfacePtWorld: Point3d, normalWorld: Vector3d, feature: FeatureData, hitData: REF ANY] = { RETURN GetFeature[featureCycler, 1]; }; PreviousFeature: PUBLIC PROC [featureCycler: FeatureCycler] RETURNS [surfacePtWorld: Point3d, normalWorld: Vector3d, feature: FeatureData, hitData: REF ANY] = { RETURN GetFeature[featureCycler, -1]; }; GetFeature: PUBLIC PROC [featureCycler: FeatureCycler, move: [-1..1]] RETURNS [surfacePtWorld: Point3d, normalWorld: Vector3d, feature: FeatureData, hitData: REF ANY] = { IF featureCycler.count = 0 THEN { -- return a backdrop position parallel: BOOL; cameraPt: Point2d _ featureCycler.cameraPt; camera: Camera _ featureCycler.camera; [surfacePtWorld, normalWorld, parallel] _ RayMeetsCentralCameraPlane[cameraPt, camera]; feature _ NIL; hitData _ NIL; } ELSE { featureCycler.index _ (featureCycler.index + move + featureCycler.count) MOD featureCycler.count; [surfacePtWorld, normalWorld, feature, hitData] _ PrepareWinner[featureCycler.nearVEF, featureCycler.index]; }; }; MultiFacesPreferred: PROC [cameraPt: Point2d, t: REAL, alignBag: AlignBag, sceneBag: TriggerBag, svData: SVData] RETURNS [nearVEF: NearVertexEdgeAndFaces, count: NAT] = { camera: Camera _ svData.camera; bestFaces: BestPoints; [bestFaces, count] _ FacesInNeighborhoodPlus[alignBag, sceneBag, cameraPt, svData, t]; IF count > 0 THEN { nearVEF _ NEW[NearVertexEdgeAndFacesObj[1]]; nearVEF[0] _ bestFaces[0]; }; }; MultiLinesPreferred: PUBLIC PROC [cameraPt: Point2d, t: REAL, alignBag: AlignBag, sceneBag: TriggerBag, svData: SVData, intersections: BOOL, maxDimension: [0..2] _ 2] RETURNS [nearVEF: NearVertexEdgeAndFaces, count: NAT] = { <> bestVerts, bestEdges, bestFaces: BestPoints; vertCount, edgeCount, faceCount, veCount: NAT; IF EmptyBag[alignBag] AND EmptyTriggers[sceneBag] THEN RETURN[NIL, 0]; IF maxDimension >= 2 THEN { [bestFaces, faceCount] _ FacesInNeighborhoodPlus[alignBag, sceneBag, cameraPt, svData, t]; SortFaces[bestFaces, faceCount]; } ELSE faceCount _ 0; [bestEdges, edgeCount] _ EdgesInNeighborhoodPlus[alignBag, sceneBag, cameraPt, svData, t, 0]; SortCurves[bestEdges, edgeCount]; [bestVerts, vertCount] _ VertsInNeighborhoodPlus[bestEdges, edgeCount, alignBag, sceneBag, cameraPt, t, svData, FALSE]; SortPoints[bestVerts, vertCount]; veCount _ MIN[vertCount + edgeCount, maxFeatures]; count _ MIN[veCount + faceCount, maxFeatures]; nearVEF _ NEW[NearVertexEdgeAndFacesObj[count]]; MergePointsAndCurves[bestVerts, vertCount, bestEdges, edgeCount, nearVEF, veCount]; MergeVEWithFaces[bestFaces, faceCount, nearVEF, veCount, maxFeatures]; }; MultiPointsPreferred: PUBLIC PROC [cameraPt: Point2d, t: REAL, alignBag: AlignBag, sceneBag: TriggerBag, svData: SVData, intersections: BOOL, maxDimension: [0..2] _ 2] RETURNS [nearVEF: NearVertexEdgeAndFaces, count: NAT] = { bestVerts, bestEdges, bestFaces: BestPoints; vertCount, edgeCount, faceCount, veCount: NAT; innerR: REAL _ t/2.0; IF EmptyBag[alignBag] AND EmptyTriggers[sceneBag] THEN RETURN[NIL, 0]; IF maxDimension >= 2 THEN { [bestFaces, faceCount] _ FacesInNeighborhoodPlus[alignBag, sceneBag, cameraPt, svData, t]; SortFaces[bestFaces, faceCount]; } ELSE faceCount _ 0; [bestEdges, edgeCount] _ EdgesInNeighborhoodPlus[alignBag, sceneBag, cameraPt, svData, t, innerR]; SortCurves[bestEdges, edgeCount]; [bestVerts, vertCount] _ VertsInNeighborhoodPlus[bestEdges, edgeCount, alignBag, sceneBag, cameraPt, t, svData, intersections]; SortPoints[bestVerts, vertCount]; IF vertCount > 0 AND bestVerts[0].dist < innerR THEN { count _ vertCount; nearVEF _ NEW[NearVertexEdgeAndFacesObj[count]]; NearPointsFromPoints[bestVerts, vertCount, nearVEF]; } ELSE { veCount _ MIN[vertCount + edgeCount, maxFeatures]; count _ MIN[veCount + faceCount, maxFeatures]; nearVEF _ NEW[NearVertexEdgeAndFacesObj[count]]; MergePointsAndCurves[bestVerts, vertCount, bestEdges, edgeCount, nearVEF, veCount]; MergeVEWithFaces[bestFaces, faceCount, nearVEF, veCount, maxFeatures]; }; }; FacesInNeighborhoodPlus: PROC [alignBag: AlignBag, sceneBag: TriggerBag, cameraPt: Point2d, svData: SVData, t: REAL] RETURNS [bestFaces: BestPoints, count: NAT] = { goodPoint: GoodPoint _ NEW[GoodPointObj]; camera: Camera _ svData.camera; searchDepth: SearchDepth _ SVState.GetSearchDepth[svData]; rayWorld: Ray _ GetWorldRayFromCameraPoint[cameraPt, camera]; -- from pool [goodPoint.point, goodPoint.normal, goodPoint.featureData, goodPoint.hitData] _ RayAtSurfaces[rayWorld, cameraPt, t, sceneBag, camera, searchDepth]; goodPoint.dimension _ 2; IF goodPoint.featureData = NIL THEN { count _ 0; } ELSE { count _ 1; bestFaces _ NEW[BestPointsObj[1]]; goodPoint.dist _ 0.0; bestFaces[0] _ goodPoint; }; SVRay.ReturnRayToPool[rayWorld]; }; EdgesInNeighborhoodPlus: PROC [alignBag: AlignBag, sceneBag: TriggerBag, q: Point2d, svData: SVData, t: REAL, innerR: REAL] RETURNS [h: BestPoints, curveCount: NAT] = { ProcessSlice: PROC [sliceD: SliceDescriptor, thisCurve: GoodPoint, featureData: FeatureData] = { success: BOOL; [thisCurve.dist, thisCurve.point, thisCurve.normal, thisCurve.hitData, success] _ SVAssembly.ClosestSegmentToLine[sliceD, q, line3d, t, camera]; IF success AND thisCurve.dist < t THEN { thisCurve.featureData _ featureData; thisCurve.dimension _ 1; dTol _ MergeCurve[thisCurve, h]; }; }; ProcessLine: PROC [line: Line3d, thisCurve: GoodPoint, featureData: FeatureData] = { thisCurve.dist _ SVLines3d.DistanceLineToLine[line, line3d]; IF thisCurve.dist < t THEN { pAlignment, pRay: Point3d; parallel: BOOL; thisCurve.featureData _ featureData; [pAlignment, pRay, parallel] _ SVLines3d.NearLinePoints[line, line3d]; thisCurve.point _ pAlignment; thisCurve.normal _ SVVector3d.Sub[pRay, pAlignment]; thisCurve.hitData _ NIL; dTol _ MergeCurve[thisCurve, h]; } }; line: Line3d; <> sliceD: SliceDescriptor; featureData: FeatureData; added: BOOL _ FALSE; thisCurve: GoodPoint _ NEW[GoodPointObj]; dTol: REAL _ t; camera: Camera _ svData.camera; line3d: Line3d; rayWorld: Ray _ GetWorldRayFromCameraPoint[q, camera]; -- from pool h _ BestCurvesFromPool[svData, t, innerR]; line3d _ LineFromRay[rayWorld]; <> FOR slopeLines: LIST OF FeatureData _ alignBag.slopeLines, slopeLines.rest UNTIL slopeLines = NIL DO featureData _ slopeLines.first; line _ NARROW[featureData.shape, AlignmentLine].line; ProcessLine[line, thisCurve, featureData]; ENDLOOP; <> FOR slices: LIST OF FeatureData _ sceneBag.slices, slices.rest UNTIL slices = NIL DO featureData _ slices.first; sliceD _ NARROW[featureData.shape, SliceDescriptor]; ProcessSlice[sliceD, thisCurve, featureData]; ENDLOOP; curveCount _ h.size; IF h.overflow THEN { CodeTimer.StartInt[$CurveOverflow, $Solidviews]; CodeTimer.StopInt[$CurveOverflow, $Solidviews]; }; SVRay.ReturnRayToPool[rayWorld]; }; -- end CurvesInNeighborhoodPlus VertsInNeighborhoodPlus: PROC [bestCurves: BestPoints, curveCount: NAT, alignBag: AlignBag, sceneBag: TriggerBag, q: Point2d, t: REAL, svData: SVData, intersections: BOOL] RETURNS [h: BestPoints, pointCount: NAT] = { thisPoint: GoodPoint; ProcessPoint: PROC [thisPoint: GoodPoint, featureData: FeatureData] = { -- used for the anchor dSquared: REAL; dTolSquared: REAL _ dTol*dTol; dSquared _ SVLines3d.Distance2PointToLine[thisPoint.point, line3d]; thisPoint.hitData _ NIL; IF dSquared < dTolSquared THEN { anchor: Skitter _ NARROW[featureData.shape]; thisPoint.dist _ RealFns.SqRt[dSquared]; thisPoint.normal _ Matrix3d.ZAxisOfMatrix[SVCaret.GetPosition[anchor]]; thisPoint.featureData _ featureData; dTol _ MergePoint[thisPoint, h]; }; }; ProcessSlice: PROC [sliceD: SliceDescriptor, thisPoint: GoodPoint, featureData: FeatureData] = { [thisPoint.dist, thisPoint.point, thisPoint.normal, thisPoint.hitData, success] _ SVAssembly.ClosestPointToLine[sliceD, q, line3d, dTol, camera]; IF success AND thisPoint.dist < dTol THEN { thisPoint.featureData _ featureData; thisPoint.dimension _ 0; dTol _ MergePoint[thisPoint, h]; }; }; sliceD: SliceDescriptor; featureData: FeatureData; success: BOOL _ FALSE; dTol: REAL _ t; midpoints: BOOL _ SVState.GetMidpoints[svData]; camera: Camera _ svData.camera; line3d: Line3d; rayWorld: Ray _ GetWorldRayFromCameraPoint[q, camera]; -- from pool thisPoint _ NEW[GoodPointObj]; h _ BestPointsFromPool[svData, t]; line3d _ LineFromRay[rayWorld]; <> <> <> <> FOR slices: LIST OF FeatureData _ sceneBag.slices, slices.rest UNTIL slices = NIL DO featureData _ slices.first; sliceD _ NARROW[featureData.shape, SliceDescriptor]; ProcessSlice[sliceD, thisPoint, featureData]; ENDLOOP; featureData _ alignBag.anchor; IF featureData # NIL THEN { anchor: Skitter _ NARROW[featureData.shape]; IF NOT SVCaret.Exists[anchor] THEN ERROR; thisPoint.point _ Matrix3d.OriginOfMatrix[SVCaret.GetPosition[anchor]]; ProcessPoint[thisPoint, featureData]; }; pointCount _ h.size; IF h.overflow THEN { CodeTimer.StartInt[$PointOverflow, $Solidviews]; CodeTimer.StopInt[$PointOverflow, $Solidviews]; }; SVRay.ReturnRayToPool[rayWorld]; }; <<>> MergePoint: PROC [thisPoint: GoodPoint, h: BestPoints] RETURNS [dTol: REAL] = { d: REAL _ thisPoint.dist; n: NAT = maxFeatures; BEGIN SELECT TRUE FROM h.size < n => GOTO Add; h.size = n AND d <= h.dTol => GOTO ReplaceOrOverflow; h.size = n AND d > h.dTol => GOTO Toss; -- the caller is not taking our hints and is passing us trash ENDCASE => SIGNAL Problem[msg: "Impossible case."]; EXITS Add => { h[h.size]^ _ thisPoint^; h.size _ h.size + 1; h.min _ MIN[h.min, d]; dTol _ h.dTol _ h.min+h.s; }; ReplaceOrOverflow => { iWorst: NAT; worstDist: REAL _ 0.0; <> iWorst _ 0; worstDist _ 0.0; FOR i: NAT IN [0..maxFeatures-1] DO IF h[i].dist > worstDist THEN {iWorst _ i; worstDist _ h[i].dist}; ENDLOOP; IF d < worstDist THEN { -- do the replace h[iWorst].dist _ d; h[iWorst]^ _ thisPoint^; h.bestTossed _ MIN[h.bestTossed, worstDist]; h.min _ MIN[h.min, d]; dTol _ h.dTol _ h.min+h.s; h.overflow _ h.bestTossed <= dTol; } ELSE { -- toss the new item dTol _ h.dTol; h.bestTossed _ MIN[h.bestTossed, d]; h.overflow _ TRUE; }; }; Toss => { dTol _ h.dTol; h.bestTossed _ MIN[h.bestTossed, d]; h.overflow _ h.bestTossed <= dTol; }; END; }; MergeCurve: PROC [thisPoint: GoodPoint, h: BestPoints] RETURNS [dTol: REAL] = { d: REAL _ thisPoint.dist; n: NAT = maxFeatures; BEGIN SELECT TRUE FROM h.size < n => GOTO Add; h.size = n AND d <= h.dTol => GOTO ReplaceOrOverflow; h.size = n AND d > h.dTol => GOTO Toss; -- the caller is not taking our hints and is passing us trash ENDCASE => SIGNAL Problem[msg: "Impossible case."]; EXITS Add => { h[h.size]^ _ thisPoint^; h.size _ h.size + 1; h.min _ MIN[h.min, d]; dTol _ h.dTol _ MAX[h.min+h.s, h.innerR]; }; ReplaceOrOverflow => { iWorst: NAT; worstDist: REAL _ 0.0; <> iWorst _ 0; worstDist _ 0.0; FOR i: NAT IN [0..maxFeatures-1] DO IF h[i].dist > worstDist THEN {iWorst _ i; worstDist _ h[i].dist}; ENDLOOP; IF d < worstDist THEN { -- do the replace h[iWorst].dist _ d; h[iWorst]^ _ thisPoint^; h.bestTossed _ MIN[h.bestTossed, worstDist]; h.min _ MIN[h.min, d]; dTol _ h.dTol _ MAX[h.min+h.s, h.innerR]; h.overflow _ h.bestTossed <= dTol; } ELSE { -- toss the new item dTol _ h.dTol; h.bestTossed _ MIN[h.bestTossed, d]; h.overflow _ TRUE; }; }; Toss => { dTol _ h.dTol; h.bestTossed _ MIN[h.bestTossed, d]; h.overflow _ h.bestTossed <= dTol; }; END; }; NearPointsFromPoints: PROC [bestPoints: BestPoints, pointCount: NAT, nearVEF: NearVertexEdgeAndFaces] = { FOR i: NAT IN [0..pointCount-1] DO nearVEF[i] _ bestPoints[i]; ENDLOOP; }; MergePointsAndCurves: PROC [bestPoints: BestPoints, pointCount: NAT, bestCurves: BestPoints, curveCount: NAT, nearVEF: NearVertexEdgeAndFaces, count: NAT] = { <> pointIndex, curveIndex: NAT; pointDist, curveDist: REAL; pointIndex _ curveIndex _ 0; FOR i: NAT IN [0..count-1] DO IF pointIndex >= pointCount THEN GOTO NoMorePoints; IF curveIndex >= curveCount THEN GOTO NoMoreCurves; pointDist _ bestPoints[i].dist; curveDist _ bestCurves[i].dist; IF pointDist <= curveDist THEN { nearVEF[i] _ bestPoints[pointIndex]; pointIndex _ pointIndex + 1; } ELSE { nearVEF[i] _ bestCurves[curveIndex]; curveIndex _ curveIndex + 1; }; REPEAT NoMorePoints => { -- finish up with Curves data FOR k: NAT _ i, k+1 UNTIL k >= count DO nearVEF[k] _ bestCurves[curveIndex]; curveIndex _ curveIndex + 1; ENDLOOP}; NoMoreCurves => { -- finish up with points data FOR k: NAT _ i, k+1 UNTIL k >= count DO nearVEF[k] _ bestPoints[pointIndex]; pointIndex _ pointIndex + 1; ENDLOOP}; ENDLOOP; }; MergeVEWithFaces: PROC [bestFaces: BestPoints, faceCount: NAT, nearVEF: NearVertexEdgeAndFaces, oldCount, maxCount: NAT] = { <> faceIndex, totalCount: NAT; totalCount _ MIN[oldCount + faceCount, maxCount]; faceIndex _ 0; FOR i: NAT IN [oldCount..totalCount-1] DO nearVEF[i] _ bestFaces[faceIndex]; faceIndex _ faceIndex + 1; ENDLOOP; }; SortPoints: PROC [bestPoints: BestPoints, pointCount: NAT] = { <> temp: GoodPointObj; FOR i: NAT IN [0..pointCount-2] DO FOR j: NAT IN [1..pointCount-i-1] DO IF bestPoints[j-1].dist > bestPoints[j].dist THEN { temp _ bestPoints[j]^; bestPoints[j]^ _ bestPoints[j-1]^; bestPoints[j-1]^ _ temp; }; ENDLOOP; ENDLOOP; }; SortCurves: PROC [bestCurves: BestPoints, curveCount: NAT] = { <> temp: GoodPointObj; FOR i: NAT IN [0..curveCount-2] DO FOR j: NAT IN [1..curveCount-i-1] DO IF bestCurves[j-1].dist > bestCurves[j].dist THEN { temp _ bestCurves[j]^; bestCurves[j]^ _ bestCurves[j-1]^; bestCurves[j-1]^ _ temp; }; ENDLOOP; ENDLOOP; }; SortFaces: PROC [bestFaces: BestPoints, faceCount: NAT] = { }; <<>> PointsOrSurfaces: PUBLIC PROC [cameraPt: Point2d, t: REAL, sceneBag: TriggerBag, camera: Camera, searchDepth: SearchDepth, gravityType: GravityType] RETURNS [surfacePtWorld: Point3d, normalWorld: Vector3d, feature: FeatureData, hitData: REF ANY] = { pointsPtWorld: Point3d; pointsNormalWorld: Vector3d; pointsFeature: FeatureData; parallel: BOOL; pointsHitData: REF ANY; rayWorld: Ray _ GetWorldRayFromCameraPoint[cameraPt, camera]; -- from pool [surfacePtWorld, normalWorld, feature, hitData] _ RayAtSurfaces[rayWorld, cameraPt, t, sceneBag, camera, searchDepth]; [pointsPtWorld, pointsNormalWorld, pointsFeature, pointsHitData] _ RayAtPoints[rayWorld, cameraPt, t, sceneBag, camera, gravityType]; IF pointsFeature # NIL AND WithinTolerance[pointsPtWorld, rayWorld, t] THEN { surfacePtWorld _ pointsPtWorld; IF pointsFeature # feature THEN { normalWorld _ pointsNormalWorld; feature _ pointsFeature; }; hitData _ pointsHitData; } ELSE IF feature # NIL THEN { } ELSE { [surfacePtWorld, normalWorld, parallel] _ RayMeetsCentralCameraPlane[cameraPt, camera]; hitData _ NIL; }; SVRay.ReturnRayToPool[rayWorld]; }; LineFromRay: PROC [ray: Ray] RETURNS [line: Line3d] = { basePt: Point3d; direction: Vector3d; [basePt, direction] _ SVRay.GetWorldRay[ray]; line _ SVLines3d.LineFromPointAndVector[basePt, direction]; }; WithinTolerance: PROC [point: Point3d, rayWorld: Ray, t: REAL] RETURNS [BOOL] = { line: Line3d; dist: REAL; line _ LineFromRay[rayWorld]; dist _ SVLines3d.DistancePointToLine[point, line]; RETURN[dist <= t]; }; GetWorldRayFromCameraPoint: PROC [cameraPt: Point2d, camera: Camera] RETURNS [rayWorld: Ray] = { rayCamera: Ray; cameraWorld: Matrix4by4 _ CoordSys.WRTWorld[camera.coordSys]; rayCamera _ SVRay.GetRayFromPool[]; -- allocates ray from pool SVRay.StuffCameraRay[rayCamera, cameraPt, camera]; rayWorld _ SVRay.TransformRayToWorld[rayCamera, cameraWorld]; -- allocates ray from pool SVRay.ReturnRayToPool[rayCamera]; }; RayAtSurfaces: PROC [rayWorld: Ray, cameraPt: Point2d, t: REAL, sceneBag: TriggerBag, camera: Camera, searchDepth: SearchDepth] RETURNS [surfacePtWorld: Point3d, normalWorld: Vector3d, feature: FeatureData, hitData: REF ANY] = { class: Classification; primitive: Primitive; assembly: Slice; class _ SingleRay[rayWorld, cameraPt, sceneBag, camera, TRUE, NIL]; -- class from pool IF sceneBag.scene # NIL THEN [assembly, primitive] _ AssemblyAndPrimitiveAtSearchDepth[class, searchDepth, sceneBag.scene.assembly]; IF class.count > 0 THEN [surfacePtWorld, normalWorld] _ SurfacePointAndNormalAtSearchDepth[class, rayWorld, searchDepth, sceneBag.scene.assembly]; feature _ FindFeature[assembly, sceneBag]; hitData _ HitDataAtSearchDepth[class, searchDepth, sceneBag.scene.assembly]; SVCastRays.ReturnClassToPool[class]; }; FindFeature: PROC [assembly: Slice, sceneBag: TriggerBag] RETURNS [feature: FeatureData] = { sliceD: SliceDescriptor; IF assembly = NIL THEN RETURN[NIL]; FOR list: LIST OF FeatureData _ sceneBag.slices, list.rest UNTIL list = NIL DO sliceD _ NARROW[list.first.shape]; IF sliceD.slice = assembly THEN RETURN[list.first]; ENDLOOP; RETURN[NIL]; }; AssemblyAndPrimitiveAtSearchDepth: PROC [class: Classification, searchDepth: SearchDepth, root: Slice] RETURNS [assembly: Slice, primitive: Primitive] = { SELECT searchDepth FROM first => [assembly, primitive] _ SVCastRays.NthAssemblyAndPrimitive[class, 1]; lastOfFirst => [assembly, primitive] _ SVCastRays.NthAssemblyAndPrimitive[class, 1]; last => [assembly, primitive] _ SVCastRays.NthAssemblyAndPrimitive[class, class.count]; lastOfLevel1 => [assembly, primitive] _ SVCastRays.LastTopLevelAssemAndPrim[class, root]; ENDCASE => ERROR; }; SurfacePointAndNormalAtSearchDepth: PROC [class: Classification, rayWorld: Ray, searchDepth: SearchDepth, root: Slice] RETURNS [surfacePtWorld: Point3d, normalWorld: Vector3d] = { SELECT searchDepth FROM first => [surfacePtWorld, normalWorld] _ SVCastRays.NthSurfacePointAndNormal[class, rayWorld, 1]; lastOfFirst => [surfacePtWorld, normalWorld] _ SVCastRays.LastOfFirstPointAndNormal[class, rayWorld]; last => IF class.count = 0 THEN ERROR ELSE [surfacePtWorld, normalWorld] _ SVCastRays.NthSurfacePointAndNormal[class, rayWorld, class.count]; lastOfLevel1 => [surfacePtWorld, normalWorld] _ SVCastRays.LastTopLevelPointAndNormal[class, rayWorld, root]; ENDCASE => ERROR; }; HitDataAtSearchDepth: PROC [class: Classification, searchDepth: SearchDepth, root: Slice] RETURNS [hitData: REF ANY] = { SELECT searchDepth FROM first => hitData _ SVCastRays.NthHitData[class, 1]; lastOfFirst => hitData _ SVCastRays.LastOfFirstHitData[class]; last => IF class.count = 0 THEN ERROR ELSE hitData _ SVCastRays.NthHitData[class, class.count]; lastOfLevel1 => hitData _ SVCastRays.LastTopLevelHitData[class, root]; ENDCASE => ERROR; }; SingleRay: PUBLIC PROC [rayWorld: Ray, cameraPt: Point2d, sceneBag: TriggerBag, camera: Camera, consolidate: BOOL _ TRUE, feedback: FeedbackData, makeStream: BOOL _ FALSE] RETURNS [class: Classification] = { tree: CSGTree _ sceneBag.tree; topNode: REF ANY; IF tree = NIL THEN { class _ SVCastRays.GetClassFromPool[]; SVCastRays.MakeClassAMiss[class]; RETURN; }; IF tree.outOfDate THEN { tree _ DisplayListToTree.AssemblyToTree[sceneBag.scene.assembly, sceneBag.scene, camera, tree, sceneBag.ignoreMoving]; [] _ Preprocess3d.PreprocessForInteraction[tree, camera]; tree.outOfDate _ FALSE; }; topNode _ tree.son; class _ SVCastRays.RayCast[cameraPt, rayWorld, topNode, consolidate, feedback, makeStream, 0]; }; -- end of SingleRay RayAtPoints: PROC [ray: Ray, cameraPt: Point2d, t: REAL, sceneBag: TriggerBag, camera: Camera, gravityType: GravityType] RETURNS [bestPoint: Point3d, bestNormal: Vector3d, bestFeature: FeatureData, bestHitData: REF ANY] = { line3d: Line3d; bestDist, thisDist: REAL; pointWorld: Point3d; normalWorld: Vector3d; feature: FeatureData; sliceD: SliceDescriptor; hitData: REF ANY; success: BOOL; scene: Scene _ sceneBag.scene; line3d _ LineFromRay[ray]; bestDist _ Real.LargestNumber; bestFeature _ NIL; IF gravityType = linesPreferred THEN { FOR list: LIST OF FeatureData _ sceneBag.slices, list.rest UNTIL list = NIL DO feature _ list.first; sliceD _ NARROW[feature.shape]; [thisDist, pointWorld, normalWorld, hitData, success] _ SVAssembly.ClosestSegmentToLine[sliceD, cameraPt, line3d, t, camera]; IF success AND thisDist < bestDist THEN { bestDist _ thisDist; bestPoint _ pointWorld; bestNormal _ normalWorld; bestHitData _ hitData; bestFeature _ feature; }; ENDLOOP; }; FOR list: LIST OF FeatureData _ sceneBag.slices, list.rest UNTIL list = NIL DO feature _ list.first; sliceD _ NARROW[feature.shape]; [thisDist, pointWorld, normalWorld, hitData, success] _ SVAssembly.ClosestPointToLine[sliceD, cameraPt, line3d, t, camera]; IF success AND thisDist < bestDist THEN { bestDist _ thisDist; bestPoint _ pointWorld; bestNormal _ normalWorld; bestHitData _ hitData; bestFeature _ feature; }; ENDLOOP; <> }; RayMeetsCameraPlane: PROC [cameraPt: Point2d, depth: REAL, camera: Camera] RETURNS [planePtCamera: Point3d, parallel: BOOL] = { rayWorld: Ray; worldCamera: Matrix4by4; planePtWorld: Point3d; rayWorld _ SVRay.GetRayFromPool[]; SVRay.StuffWorldRayFromCamera[rayWorld, cameraPt, camera]; [planePtWorld, parallel] _ SVCastRays.RayMeetsPlaneOfCoordSystem[camera.coordSys, rayWorld, 3, depth]; worldCamera _ CoordSys.FindAInTermsOfB[CoordSys.Parent[camera.coordSys], camera.coordSys]; planePtCamera _ Matrix3d.Update[planePtWorld, worldCamera]; SVRay.ReturnRayToPool[rayWorld]; }; RayMeetsCentralCameraPlane: PROC [cameraPt: Point2d, camera: Camera] RETURNS [pointWorld: Point3d, normalWorld: Vector3d, parallel: BOOL] = { worldCAMERA: Matrix4by4 _ CoordSys.FindWorldInTermsOf[camera.coordSys]; originCAMERA: Point3d _ Matrix3d.OriginOfMatrix[worldCAMERA]; depth: REAL _ originCAMERA[3]; rayWorld: Ray; rayWorld _ SVRay.GetRayFromPool[]; SVRay.StuffWorldRayFromCamera[rayWorld, cameraPt, camera]; [pointWorld, parallel] _ SVCastRays.RayMeetsPlaneOfCoordSystem[camera.coordSys, rayWorld, 3, depth]; normalWorld _ Matrix3d.ZAxisOfMatrix[CoordSys.WRTWorld[camera.coordSys]]; SVRay.ReturnRayToPool[rayWorld]; }; NewGravityPool: PUBLIC PROC [] RETURNS [gravityPool: REF ANY] = { pool: MultiGravityPool _ NEW[MultiGravityPoolObj]; pool.distances _ NEW[NearDistancesObj[maxFeatures]]; pool.features _ NEW[NearFeaturesObj[maxFeatures]]; pool.bestpoints _ NEW[BestPointsObj[maxFeatures]]; pool.bestcurves _ NEW[BestPointsObj[maxFeatures]]; FOR i: NAT IN [0..maxFeatures) DO pool.bestpoints[i] _ NEW[GoodPointObj]; pool.bestcurves[i] _ NEW[GoodPointObj]; ENDLOOP; RETURN[pool]; }; BestCurvesFromPool: PROC [svData: SVData, t: REAL, innerR: REAL] RETURNS [h: BestPoints] = { h _ NARROW[svData.gravityPool, MultiGravityPool].bestcurves; h.size _ 0; h.max _ 0; h.min _ Real.LargestNumber; h.dTol _ t; h.innerR _ innerR; h.s _ 0.072; -- 1/1000 inches h.bestTossed _ Real.LargestNumber; h.overflow _ FALSE; FOR i: NAT IN [0..maxFeatures-1] DO h[i].dist _ Real.LargestNumber; h[i].featureData _ NIL; ENDLOOP; }; BestPointsFromPool: PROC [svData: SVData, t: REAL] RETURNS [h: BestPoints] = { h _ NARROW[svData.gravityPool, MultiGravityPool].bestpoints; h.size _ 0; h.max _ 0; h.min _ Real.LargestNumber; h.dTol _ t; h.s _ 0.072; -- 1/1000 inches h.bestTossed _ Real.LargestNumber; h.overflow _ FALSE; FOR i: NAT IN [0..maxFeatures-1] DO h[i].dist _ Real.LargestNumber; h[i].featureData _ NIL; ENDLOOP; }; Init: PROC = { }; InitStats: PROC = { interval: CodeTimer.Interval; interval _ CodeTimer.CreateInterval[$RayMap]; CodeTimer.AddInt[interval, $Solidviews]; }; InitStats[]; Init[]; END.