<> <> <> <> DIRECTORY BasicObject3d, CastRays, CoordSys, CSG, DisplayList3d, DisplayListToTree, Graphics, GraphicsColor, Matrix3d, Preprocess3d, Rope, SV2d, SV3d, SVEditUser, SVError, SVInterfaceTypes, SVModelTypes, SVRayTypes, SVSceneTypes, SVSelections, SVTransforms, SVVector3d, SVViewerInput, SVViewerTools, SVViewerUser, TIPUser, ViewerClasses, ViewerTools; SVViewerInputImplC: PROGRAM IMPORTS CastRays, CoordSys, CSG, DisplayList3d, DisplayListToTree, Matrix3d, Preprocess3d, SVEditUser, SVError, SVSelections, SVTransforms, SVVector3d, SVViewerInput, SVViewerTools, SVViewerUser, ViewerTools EXPORTS SVViewerInput = BEGIN Artwork: TYPE = SVModelTypes.Artwork; ArtworkToolData: TYPE = SVInterfaceTypes.ArtworkToolData; Assembly: TYPE = SVSceneTypes.Assembly; AssemblyList: TYPE = SVSceneTypes.AssemblyList; BoundBox: TYPE = SVModelTypes.BoundBox; BoundSphere: TYPE = SVModelTypes.BoundSphere; Camera: TYPE = SVModelTypes.Camera; Classification: TYPE = SVRayTypes.Classification; Color: TYPE = GraphicsColor.Color; CoordSystem: TYPE = SVModelTypes.CoordSystem; CSGTree: TYPE = SVRayTypes.CSGTree; CylinderRec: TYPE = BasicObject3d.CylinderRec; EditToolData: TYPE = SVInterfaceTypes.EditToolData; FileCamera: TYPE = SVSceneTypes.FileCamera; FrameBox: TYPE = SVModelTypes.FrameBox; MasterObject: TYPE = SVSceneTypes.MasterObject; Matrix4by4: TYPE = SV3d.Matrix4by4; Point2d: TYPE = SV2d.Point2d; Point3d: TYPE = SV3d.Point3d; Primitive: TYPE = SVRayTypes.Primitive; Ray: TYPE = SVRayTypes.Ray; Scene: TYPE = SVSceneTypes.Scene; SearchDepth: TYPE = SVInterfaceTypes.SearchDepth; Selection: TYPE = SVInterfaceTypes.Selection; SelectionGenerator: TYPE = SVInterfaceTypes.SelectionGenerator; Shape: TYPE = SVSceneTypes.Shape; SkitterMode: TYPE = SVInterfaceTypes.SkitterMode; ToolData: TYPE = SVSceneTypes.ToolData; TrigLine: TYPE = SV2d.TrigLine; Vector: TYPE = SV3d.Vector; ViewerToolData: TYPE = SVInterfaceTypes.ViewerToolData; JackPivotX: PUBLIC PROC [viewerToolData: ViewerToolData, degrees: REAL] = { <> editToolData: EditToolData _ viewerToolData.editToolData; scene: Scene _ viewerToolData.scene; targetSel: Selection; coincident: Assembly; targetSel _ SVSelections.TopTarget[]; IF targetSel = NIL THEN RETURN; coincident _ targetSel.coincident; SVSelections.ComplementSelection[viewerToolData, targetSel]; <> SVTransforms.XRotate[coincident.coordSys, coincident.coordSys, degrees, TRUE]; SVSelections.ComplementSelection[viewerToolData, targetSel]; }; JackPivotY: PUBLIC PROC [viewerToolData: ViewerToolData, degrees: REAL] = { <> editToolData: EditToolData _ viewerToolData.editToolData; scene: Scene _ viewerToolData.scene; targetSel: Selection; coincident: Assembly; targetSel _ SVSelections.TopTarget[]; IF targetSel = NIL THEN RETURN; coincident _ targetSel.coincident; SVSelections.ComplementSelection[viewerToolData, targetSel]; <> SVTransforms.YRotate[coincident.coordSys, coincident.coordSys, degrees, TRUE]; SVSelections.ComplementSelection[viewerToolData, targetSel]; }; JackPivotZ: PUBLIC PROC [viewerToolData: ViewerToolData, degrees: REAL] = { <> editToolData: EditToolData _ viewerToolData.editToolData; scene: Scene _ viewerToolData.scene; targetSel: Selection; coincident: Assembly; targetSel _ SVSelections.TopTarget[]; IF targetSel = NIL THEN RETURN; coincident _ targetSel.coincident; SVSelections.ComplementSelection[viewerToolData, targetSel]; <> SVTransforms.ZRotate[coincident.coordSys, coincident.coordSys, degrees, TRUE]; SVSelections.ComplementSelection[viewerToolData, targetSel]; }; SourcePivotX: PUBLIC PROC [viewerToolData: ViewerToolData, degrees: REAL] = { <> editToolData: EditToolData _ viewerToolData.editToolData; scene: Scene _ viewerToolData.scene; sourceSel: Selection; jack, indirect: Assembly; sourceSel _ SVSelections.TopMovee[]; IF sourceSel = NIL THEN RETURN; SELECT sourceSel.referentType FROM hook => { jack _ sourceSel.coincident; indirect _ sourceSel.indirect; }; coordSys => { jack _ sourceSel.coincident; indirect _ sourceSel.coincident; }; ENDCASE => ERROR; SVEditUser.PaintAssemblyAllViewers[SVViewerUser.DrawAssemblyXOR, editToolData, scene, indirect]; SVTransforms.XRotate[indirect.coordSys, jack.coordSys, degrees, TRUE]; SVEditUser.PaintAssemblyAllViewers[SVViewerUser.DrawAssemblyXOR, editToolData, scene, indirect]; }; SourcePivotY: PUBLIC PROC [viewerToolData: ViewerToolData, degrees: REAL] = { <> editToolData: EditToolData _ viewerToolData.editToolData; scene: Scene _ viewerToolData.scene; sourceSel: Selection; jack, indirect: Assembly; sourceSel _ SVSelections.TopMovee[]; IF sourceSel = NIL THEN RETURN; SELECT sourceSel.referentType FROM hook => { jack _ sourceSel.coincident; indirect _ sourceSel.indirect; }; coordSys => { jack _ sourceSel.coincident; indirect _ sourceSel.coincident; }; ENDCASE => ERROR; SVEditUser.PaintAssemblyAllViewers[SVViewerUser.DrawAssemblyXOR, editToolData, scene, indirect]; SVTransforms.YRotate[indirect.coordSys, jack.coordSys, degrees, TRUE]; SVEditUser.PaintAssemblyAllViewers[SVViewerUser.DrawAssemblyXOR, editToolData, scene, indirect]; }; SourcePivotZ: PUBLIC PROC [viewerToolData: ViewerToolData, degrees: REAL] = { <> editToolData: EditToolData _ viewerToolData.editToolData; scene: Scene _ viewerToolData.scene; sourceSel: Selection; jack, indirect: Assembly; sourceSel _ SVSelections.TopMovee[]; IF sourceSel = NIL THEN RETURN; SELECT sourceSel.referentType FROM hook => { jack _ sourceSel.coincident; indirect _ sourceSel.indirect; }; coordSys => { jack _ sourceSel.coincident; indirect _ sourceSel.coincident; }; ENDCASE => ERROR; SVEditUser.PaintAssemblyAllViewers[SVViewerUser.DrawAssemblyXOR, editToolData, scene, indirect]; SVTransforms.ZRotate[indirect.coordSys, jack.coordSys, degrees, TRUE]; SVEditUser.PaintAssemblyAllViewers[SVViewerUser.DrawAssemblyXOR, editToolData, scene, indirect]; }; ArrowShoot: PUBLIC PROC [] = { <> source, target: Assembly; sourceSel, firstTargetSel: Selection; viewerToolData: ViewerToolData; sourceSel _ SVSelections.PopMovee[]; IF sourceSel = NIL THEN RETURN; source _ sourceSel.coincident; viewerToolData _ sourceSel.viewerToolData; firstTargetSel _ SVSelections.PopTarget[]; IF firstTargetSel = NIL THEN RETURN; FOR targetSel: Selection _ firstTargetSel, SVSelections.NextTarget[] UNTIL targetSel = NIL DO IF targetSel.viewerToolData # sourceSel.viewerToolData THEN { SVError.Append["Can't shoot arrows between viewers.", TRUE, TRUE]; SVError.Blink[]; LOOP; }; target _ targetSel.coincident; ArrowShootAux[source.coordSys, target.coordSys, viewerToolData]; ENDLOOP; SVEditUser.PaintSceneAllViewers[SVViewerUser.EraseAndDrawSceneEtc, viewerToolData.editToolData, viewerToolData.scene]; }; ArrowShootAux: PROC [sourceCS, targetCS: CoordSystem, viewerToolData: ViewerToolData] = { <> sOriginWorld, tOriginWorld: Point3d; tree: CSGTree; ray: Ray; class: Classification; t: REAL; worldDirection, primitiveNormal, worldNormal: Vector; selectionMat: Matrix4by4; surfacePtInWorld: Point3d; primitive: Primitive; assembly: Assembly; scene: Scene; camera: Camera; scene _ viewerToolData.scene; camera _ viewerToolData.camera; tree _ DisplayListToTree.AssemblyToTree[scene.assembly, scene, camera]; [] _ Preprocess3d.PreprocessForSlice[tree, camera]; sOriginWorld _ Matrix3d.OriginOfMatrix[sourceCS.wrtWorld]; tOriginWorld _ Matrix3d.OriginOfMatrix[targetCS.wrtWorld]; ray _ CSG.GetRayFromPool[]; worldDirection _ SVVector3d.Sub[tOriginWorld, sOriginWorld]; CSG.StuffWorldRay[ray, sOriginWorld, worldDirection, camera]; class _ CastRays.RayCastBoundingSpheres[ray, tree.son]; FOR i: NAT IN [1..class.count] DO t _ class.params[i]; -- the parameter of the ray intersection primitiveNormal _ class.normals[i]; primitive _ class.primitives[i]; worldNormal _ Matrix3d.UpdateVectorWithInverse[primitive.worldWRTPrim, primitiveNormal]; surfacePtInWorld[1] _ sOriginWorld[1] + t*worldDirection[1]; surfacePtInWorld[2] _ sOriginWorld[2] + t*worldDirection[2]; surfacePtInWorld[3] _ sOriginWorld[3] + t*worldDirection[3]; assembly _ NARROW[primitive.assembly]; SVViewerInput.ComplementSkitter[]; -- erase any old skitter selectionMat _ MakeAlignedMat[worldNormal, surfacePtInWorld, assembly.coordSys]; SVSelections.UpdateSkitter[assembly, primitive, viewerToolData]; SVSelections.PositionSkitter[[0,0], selectionMat]; SVSelections.SetModeSkitter[surface]; SVViewerInput.ComplementSkitter[]; -- draw new skitter SVViewerInput.SkitterMakes[]; ENDLOOP; CastRays.ReturnClassToPool[class]; CSG.ReturnRayToPool[ray]; }; Sign: PRIVATE PROC [r: REAL] RETURNS [INT] = { IF r = 0.0 THEN RETURN[2]; IF r < 0.0 THEN RETURN[-1] ELSE RETURN[1]; }; AntiParallel: PRIVATE PROC [v1, v2: Vector] RETURNS [BOOL] = { RETURN[Sign[v1[1]] = -Sign[v2[1]] OR Sign[v1[2]] = -Sign[v2[2]] OR Sign[v1[3]] = -Sign[v2[3]] ]; }; MakeAlignedMat: PRIVATE PROC [worldNormal: Vector, surfacePtInWorld: Point3d, cs: CoordSystem] RETURNS [mat: Matrix4by4] = TRUSTED { <> yAxisOfCS: Vector _ Matrix3d.YAxisOfMatrix[cs.wrtWorld]; xAxis: Vector; IF SVVector3d.Parallel[yAxisOfCS, worldNormal] THEN { xAxis _ Matrix3d.XAxisOfMatrix[cs.wrtWorld]; IF AntiParallel[yAxisOfCS, worldNormal] THEN xAxis _ SVVector3d.Negate[xAxis]; <> } ELSE xAxis _ SVVector3d.CrossProduct[yAxisOfCS, worldNormal]; mat _ Matrix3d.MakeMatFromZandXAxis[worldNormal, xAxis, surfacePtInWorld]; }; MoveUntilTouch: PUBLIC PROC [] = { <> source, target: Assembly; sourceSel, targetSel: Selection; viewerToolData: ViewerToolData; sourceSel _ SVSelections.PopMovee[]; IF sourceSel = NIL THEN RETURN; source _ sourceSel.coincident; viewerToolData _ sourceSel.viewerToolData; targetSel _ SVSelections.PopTarget[]; IF targetSel = NIL THEN RETURN; IF targetSel.viewerToolData # sourceSel.viewerToolData THEN { SVError.Append["Can't skewer between viewers.", TRUE, TRUE]; SVError.Blink[]; RETURN; }; target _ targetSel.coincident; MoveUntilTouchAux[source, target, viewerToolData]; SVEditUser.PaintSceneAllViewers[SVViewerUser.EraseAndDrawSceneEtc, viewerToolData.editToolData, viewerToolData.scene]; }; MoveUntilTouchAux: PROC [source, target: Assembly, viewerToolData: ViewerToolData] = { <> sOriginWorld, tOriginWorld: Point3d; tree, sourceTree: CSGTree; worldDirection: Vector; scene: Scene; camera: Camera; boundSphere: BoundSphere; R: REAL; basisMat: Matrix4by4; scene _ viewerToolData.scene; camera _ viewerToolData.camera; sourceTree _ DisplayListToTree.AssemblyToTree[source, scene, camera]; boundSphere _ Preprocess3d.PreprocessForSlice[sourceTree, camera]; R _ boundSphere.radius; tree _ DisplayListToTree.AssemblyToTree[scene.assembly, scene, camera]; [] _ Preprocess3d.PreprocessForSlice[tree, camera]; sOriginWorld _ Matrix3d.OriginOfMatrix[source.coordSys.wrtWorld]; tOriginWorld _ Matrix3d.OriginOfMatrix[target.coordSys.wrtWorld]; worldDirection _ SVVector3d.Sub[tOriginWorld, sOriginWorld]; <> basisMat _ Matrix3d.MakeHorizontalMatFromZAxis[worldDirection, sOriginWorld]; MoveUntilTouchAuxAux[source, scene.coordSysRoot, basisMat, worldDirection, R, camera, tree]; }; -- end of MoveUntilTouchAux MoveUntilTouchAuxAux: PROC [source: Assembly, worldCS: CoordSystem, basisMat: Matrix4by4, worldDirection: Vector, R: REAL, camera: Camera, tree: CSGTree] = { ray: Ray; minDeltaT, lastSourceT, firstOtherT: REAL; class: Classification; success, someData: BOOL; basisBasePt, worldBasePt: Point3d; moveVector: Vector; raysPerHalfSide: NAT = 20; raysPerHalfSideF: REAL = 20.0; ray _ CSG.GetRayFromPool[]; someData _ FALSE; FOR i: INT IN [-raysPerHalfSide..raysPerHalfSide-1] DO basisBasePt[1] _ (i+0.5)*R/raysPerHalfSideF; FOR j: INT IN [-raysPerHalfSide..raysPerHalfSide-1] DO basisBasePt[2] _ (j+0.5)*R/raysPerHalfSideF; basisBasePt[3] _ 0.0; worldBasePt _ Matrix3d.Update[basisMat, basisBasePt]; CSG.StuffWorldRay[ray, worldBasePt, worldDirection, camera]; class _ CastRays.RayCastBoundingSpheres[ray, tree.son, FALSE]; [lastSourceT, firstOtherT, success] _ SourceAndOtherT[class, source]; IF NOT success THEN { CastRays.ReturnClassToPool[class]; LOOP; }; IF firstOtherT < lastSourceT THEN { SVError.Append["Source is already touching.",TRUE,TRUE]; SVError.Blink[]; RETURN; }; IF NOT someData THEN { minDeltaT _ firstOtherT-lastSourceT; someData _ TRUE; } ELSE minDeltaT _ MIN[minDeltaT, firstOtherT-lastSourceT]; CastRays.ReturnClassToPool[class]; ENDLOOP; -- j ENDLOOP; -- i IF NOT someData THEN { SVError.Append["No Obstacles Found. Object NOT moved.",TRUE,TRUE]; SVError.Blink[]; RETURN; }; moveVector _ SVVector3d.Scale[worldDirection, minDeltaT]; SVTransforms.Translate[source.coordSys, worldCS, moveVector[1], moveVector[2], moveVector[3]]; CSG.ReturnRayToPool[ray]; }; SourceAndOtherT: PROC [class: Classification, source: Assembly] RETURNS [lastSourceT, firstOtherT: REAL, success: BOOL _ FALSE] = { sourceFound, otherFound: BOOL; thisAssembly: Assembly; sourceFound _ FALSE; otherFound _ FALSE; IF class.count = 0 THEN RETURN [0,0,FALSE]; FOR i: NAT IN [1..class.count] DO thisAssembly _ NARROW[class.primitives[i].assembly]; IF DisplayListToTree.IsSuccessorOf[thisAssembly, source] THEN { lastSourceT _ class.params[i]; sourceFound _ TRUE; } ELSE IF NOT otherFound THEN { firstOtherT _ class.params[i]; otherFound _ TRUE; }; ENDLOOP; success _ sourceFound AND otherFound; }; Skewer: PUBLIC PROC [] = { <> source, target: Assembly; sourceSel, firstTargetSel: Selection; viewerToolData: ViewerToolData; sourceSel _ SVSelections.PopMovee[]; IF sourceSel = NIL THEN RETURN; source _ sourceSel.coincident; viewerToolData _ sourceSel.viewerToolData; firstTargetSel _ SVSelections.PopTarget[]; IF firstTargetSel = NIL THEN RETURN; IF firstTargetSel.viewerToolData # sourceSel.viewerToolData THEN { SVError.Append["Can't skewer between viewers.", TRUE, TRUE]; SVError.Blink[]; RETURN; }; target _ firstTargetSel.coincident; SkewerAux[source, target, viewerToolData]; SVEditUser.PaintSceneAllViewers[SVViewerUser.EraseAndDrawSceneEtc, viewerToolData.editToolData, viewerToolData.scene]; }; SkewerAux: PROC [source, target: Assembly, viewerToolData: ViewerToolData] = { <> sOriginWorld, tOriginWorld: Point3d; tree: CSGTree; ray: Ray; class: Classification; t: REAL; directionWorld: Vector; surfacePtInWorld, lastSurfacePtInWorld: Point3d; primitive: Primitive; assembly, lastAssembly: Assembly; moveVector, totalVector, localDiff: Vector; scene: Scene; camera: Camera; sourceCS, targetCS: CoordSystem; sourceCS _ source.coordSys; targetCS _ target.coordSys; scene _ viewerToolData.scene; camera _ viewerToolData.camera; tree _ DisplayListToTree.AssemblyToTree[scene.assembly, scene, camera]; [] _ Preprocess3d.PreprocessForSlice[tree, camera]; sOriginWorld _ Matrix3d.OriginOfMatrix[sourceCS.wrtWorld]; tOriginWorld _ Matrix3d.OriginOfMatrix[targetCS.wrtWorld]; ray _ CSG.GetRayFromPool[]; directionWorld _ SVVector3d.Sub[tOriginWorld, sOriginWorld]; <> sOriginWorld _ SVVector3d.Sub[sOriginWorld, SVVector3d.Scale[directionWorld, 0.3]]; directionWorld _ SVVector3d.Scale[directionWorld, 1.6]; <> CSG.StuffWorldRay[ray, sOriginWorld, directionWorld, camera]; class _ CastRays.RayCastBoundingSpheres[ray, tree.son, FALSE]; CastRays.SortClassByPrimitive[class]; IF class.count = 0 THEN { CastRays.ReturnClassToPool[class]; CSG.ReturnRayToPool[ray]; RETURN; }; lastAssembly _ NARROW[class.primitives[1].assembly]; t _ class.params[1]; lastSurfacePtInWorld[1] _ sOriginWorld[1] + t*directionWorld[1]; lastSurfacePtInWorld[2] _ sOriginWorld[2] + t*directionWorld[2]; lastSurfacePtInWorld[3] _ sOriginWorld[3] + t*directionWorld[3]; totalVector _ [0,0,0]; FOR i: NAT IN [2..class.count] DO t _ class.params[i]; -- the parameter of the ray intersection IF t > 1.0 THEN EXIT; -- Compression occurs only BETWEEN source and target. primitive _ class.primitives[i]; surfacePtInWorld[1] _ sOriginWorld[1] + t*directionWorld[1]; surfacePtInWorld[2] _ sOriginWorld[2] + t*directionWorld[2]; surfacePtInWorld[3] _ sOriginWorld[3] + t*directionWorld[3]; assembly _ NARROW[primitive.assembly]; IF lastAssembly # assembly THEN { localDiff _ SVVector3d.Sub[lastSurfacePtInWorld, surfacePtInWorld]; moveVector _ SVVector3d.Add[localDiff, totalVector]; totalVector _ SVVector3d.Add[totalVector, localDiff]; SVTransforms.Translate[assembly.coordSys, scene.coordSysRoot, moveVector[1], moveVector[2], moveVector[3]]; }; lastAssembly _ assembly; lastSurfacePtInWorld _ surfacePtInWorld; ENDLOOP; CastRays.ReturnClassToPool[class]; CSG.ReturnRayToPool[ray]; }; AddCylinder: PUBLIC PROC [] = { <> sourceSel, firstTargetSel: Selection; sourceCS, targetCS: CoordSystem; viewerToolData: ViewerToolData; editToolData: EditToolData; radius: REAL; scene: Scene; sourceSel _ SVSelections.PopMovee[]; IF sourceSel = NIL THEN RETURN; firstTargetSel _ SVSelections.PopTarget[]; IF firstTargetSel = NIL THEN RETURN; IF sourceSel.viewerToolData # firstTargetSel.viewerToolData THEN { SVError.Append["Can't add cylinder between viewers.", TRUE, TRUE]; SVError.Blink[]; RETURN; }; viewerToolData _ sourceSel.viewerToolData; scene _ viewerToolData.scene; editToolData _ viewerToolData.editToolData; sourceCS _ sourceSel.coincident.coordSys; radius _ SVViewerTools.GetReal[editToolData.cylinderSection.radius, 20]; FOR targetSel: Selection _ firstTargetSel, SVSelections.NextTarget[] UNTIL targetSel = NIL DO targetCS _ targetSel.coincident.coordSys; AddCylinderAux[sourceCS, targetCS, radius, editToolData, scene]; ENDLOOP; SVEditUser.PaintSceneAllViewers[SVViewerUser.EraseAndDrawSceneEtc, editToolData, viewerToolData.scene]; }; AddCylinderAux: PROC [sourceCS, targetCS: CoordSystem, radius: REAL, editToolData: EditToolData, scene: Scene] = { <> cylMO: MasterObject; moFound: BOOL; cylinderRec: CylinderRec; sOriginWorld, tOriginWorld, midPointWorld: Point3d; cylWorld, superWorld, cylSuper: Matrix4by4; cylinderY: Vector; height: REAL; newAssembly, superAssembly: Assembly; success: BOOL; addSucceeds: BOOL _ TRUE; cylName: Rope.ROPE; [cylMO, moFound] _ DisplayList3d.FindObjectFromName["cylinder", scene]; IF NOT moFound THEN ERROR; cylinderRec _ NARROW[cylMO.mainBody]; sOriginWorld _ Matrix3d.OriginOfMatrix[sourceCS.wrtWorld]; tOriginWorld _ Matrix3d.OriginOfMatrix[targetCS.wrtWorld]; midPointWorld _ SVVector3d.Add[sOriginWorld, tOriginWorld]; midPointWorld _ SVVector3d.Scale[midPointWorld, 0.5]; cylinderY _ SVVector3d.Sub[tOriginWorld, sOriginWorld]; height _ SVVector3d.Magnitude[cylinderY]; cylWorld _ MakeHorizontalMatFromYAxis[cylinderY, midPointWorld]; radius _ radius/cylinderRec.radius; height _ height/cylinderRec.height; cylName _ ViewerTools.GetContents[editToolData.sceneSection.new]; cylName _ CoordSys.UniqueNameFrom[cylName, scene.coordSysRoot]; [superAssembly, success] _ SVEditUser.GetParent[editToolData]; IF NOT success THEN RETURN; superWorld _ superAssembly.coordSys.wrtWorld; cylSuper _ Matrix3d.WorldToLocal[superWorld, cylWorld]; [newAssembly, ----] _ DisplayList3d.CreateAndAddPrimitiveAssembly[cylName, "cylinder", [radius, height, radius], scene, superAssembly, cylSuper ! DisplayList3d.NameAlreadyPresent => TRUSTED { SVError.Append["This cyl name is already registered. Try another.", TRUE, TRUE]; SVError.Blink[]; addSucceeds _ FALSE; CONTINUE}; DisplayList3d.AttemptToAddSubassemblyToPrimitive => { SVError.Append["Attempt To Add Subassembly To Primitive", TRUE, TRUE]; SVError.Blink[]; addSucceeds _ FALSE; CONTINUE} ]; IF NOT addSucceeds THEN RETURN; }; -- end of AddCylinderAux MakeHorizontalMatFromYAxis: PROC [yAxis: Vector, origin: Point3d] RETURNS [mat: Matrix4by4] = { <> xAxis: Vector; IF yAxis[1] = 0 AND yAxis[3] = 0 THEN xAxis _ [1,0,0] ELSE xAxis _ SVVector3d.CrossProduct[[0,1,0], yAxis]; mat _ Matrix3d.MakeMatFromYandXAxis[yAxis, xAxis, origin]; }; END.