public TrackBallController(TrackBallController trackBallToCopy) { this.screenCenter = trackBallToCopy.screenCenter; this.rotationTrackingRadius = trackBallToCopy.rotationTrackingRadius; this.currentRotationMatrix = trackBallToCopy.currentRotationMatrix; this.currentTranslationMatrix = trackBallToCopy.currentTranslationMatrix; }
public ScaleRotateTranslate(Matrix4X4 scale, Matrix4X4 rotation, Matrix4X4 translation) { centering = Matrix4X4.Identity; this.scale = scale; this.rotation = rotation; this.translation = translation; }
public AxisAlignedBoundingBox NewTransformed(Matrix4X4 transform) { Vector3[] boundsVerts = new Vector3[8]; boundsVerts[0] = new Vector3(this[0][0], this[0][1], this[0][2]); boundsVerts[1] = new Vector3(this[0][0], this[0][1], this[1][2]); boundsVerts[2] = new Vector3(this[0][0], this[1][1], this[0][2]); boundsVerts[3] = new Vector3(this[0][0], this[1][1], this[1][2]); boundsVerts[4] = new Vector3(this[1][0], this[0][1], this[0][2]); boundsVerts[5] = new Vector3(this[1][0], this[0][1], this[1][2]); boundsVerts[6] = new Vector3(this[1][0], this[1][1], this[0][2]); boundsVerts[7] = new Vector3(this[1][0], this[1][1], this[1][2]); Vector3.Transform(boundsVerts, transform); Vector3 newMin = new Vector3(double.MaxValue, double.MaxValue, double.MaxValue); Vector3 newMax = new Vector3(double.MinValue, double.MinValue, double.MinValue); for (int i = 0; i < 8; i++) { newMin.x = Math.Min(newMin.x, boundsVerts[i].x); newMin.y = Math.Min(newMin.y, boundsVerts[i].y); newMin.z = Math.Min(newMin.z, boundsVerts[i].z); newMax.x = Math.Max(newMax.x, boundsVerts[i].x); newMax.y = Math.Max(newMax.y, boundsVerts[i].y); newMax.z = Math.Max(newMax.z, boundsVerts[i].z); } return new AxisAlignedBoundingBox(newMin, newMax); }
public TransformUndoCommand(View3DWidget view3DWidget, int meshGroupIndex, Matrix4X4 undoTransform, Matrix4X4 redoTransform) { this.view3DWidget = view3DWidget; this.meshGroupIndex = meshGroupIndex; this.undoTransform = undoTransform; this.redoTransform = redoTransform; }
public void SetPrintLevelingEquation(Vector3 position0, Vector3 position1, Vector3 position2, Vector2 bedCenter) { if (position0 == position1 || position1 == position2 || position2 == position0) { return; } Plane planeOfPoints = new Plane(position0, position1, position2); Ray ray = new Ray(new Vector3(bedCenter, 0), Vector3.UnitZ); bool inFront; double distanceToPlaneAtBedCenter = planeOfPoints.GetDistanceToIntersection(ray, out inFront); Matrix4X4 makePointsFlatMatrix = Matrix4X4.CreateTranslation(-bedCenter.x, -bedCenter.y, -distanceToPlaneAtBedCenter); makePointsFlatMatrix *= Matrix4X4.CreateRotation(planeOfPoints.planeNormal, Vector3.UnitZ); makePointsFlatMatrix *= Matrix4X4.CreateTranslation(bedCenter.x, bedCenter.y, 0);//distanceToPlaneAtBedCenter); bedLevelMatrix = Matrix4X4.Invert(makePointsFlatMatrix); { // test that the points come back as 0 zs Vector3 outPosition0 = Vector3.TransformPosition(position0, makePointsFlatMatrix); Vector3 outPosition1 = Vector3.TransformPosition(position1, makePointsFlatMatrix); Vector3 outPosition2 = Vector3.TransformPosition(position2, makePointsFlatMatrix); Vector3 printPosition0 = new Vector3(ActiveSliceSettings.Instance.GetPrintLevelSamplePosition(0), 0); Vector3 printPosition1 = new Vector3(ActiveSliceSettings.Instance.GetPrintLevelSamplePosition(1), 0); Vector3 printPosition2 = new Vector3(ActiveSliceSettings.Instance.GetPrintLevelSamplePosition(2), 0); Vector3 leveledPositon0 = Vector3.TransformPosition(printPosition0, bedLevelMatrix); Vector3 leveledPositon1 = Vector3.TransformPosition(printPosition1, bedLevelMatrix); Vector3 leveledPositon2 = Vector3.TransformPosition(printPosition2, bedLevelMatrix); } }
public Transform(IRayTraceable root, Matrix4X4 transform) { this.child = root; WorldToAxis = transform; AxisToWorld = Matrix4X4.Invert(WorldToAxis); AxisToWorld = transform; WorldToAxis = Matrix4X4.Invert(AxisToWorld); }
public DeleteUndoCommand(View3DWidget view3DWidget, int deletedIndex) { this.view3DWidget = view3DWidget; this.deletedIndex = deletedIndex; meshGroupThatWasDeleted = view3DWidget.MeshGroups[deletedIndex]; deletedTransform = view3DWidget.MeshGroupTransforms[deletedIndex]; deletedPlatingData = view3DWidget.MeshGroupExtraData[deletedIndex]; }
public void MoveToAbsolute(double x, double y, double z) { AxisToWorld[3, 0] = x; AxisToWorld[3, 1] = y; AxisToWorld[3, 2] = z; WorldToAxis = Matrix4X4.Invert(AxisToWorld); }
public CopyUndoCommand(View3DWidget view3DWidget, int newItemIndex) { this.view3DWidget = view3DWidget; this.newItemIndex = newItemIndex; meshGroupThatWasDeleted = view3DWidget.MeshGroups[newItemIndex]; newItemTransform = view3DWidget.MeshGroupTransforms[newItemIndex]; newItemPlatingData = view3DWidget.MeshGroupExtraData[newItemIndex]; }
public void SetCenteringForMeshGroup(MeshGroup meshGroup) { AxisAlignedBoundingBox bounds = meshGroup.GetAxisAlignedBoundingBox(); Vector3 boundsCenter = (bounds.maxXYZ + bounds.minXYZ) / 2; centering = Matrix4X4.CreateTranslation(-boundsCenter); // and move the translation back so the part does not move translation *= Matrix4X4.CreateTranslation(boundsCenter); }
public void MarkAsChanged() { // mark this unchecked as we don't want to throw an exception if this rolls over. unchecked { fastAABBTransform = Matrix4X4.Identity; fastAABBTransform[0, 0] = double.MinValue; changedCount++; } }
public Camera(Camera cameraToCopy) { this.cameraFOV = cameraToCopy.cameraFOV; this.distanceToCameraPlane = cameraToCopy.distanceToCameraPlane; this.axisToWorld = cameraToCopy.axisToWorld; this.widthInPixels = cameraToCopy.widthInPixels; this.heightInPixels = cameraToCopy.heightInPixels; }
private static void TransformVector(Vector3 vec, ref Matrix4X4 mat, out Vector3 result) { result.x = vec.x * mat.Row0.x + vec.y * mat.Row1.x + vec.z * mat.Row2.x; result.y = vec.x * mat.Row0.y + vec.y * mat.Row1.y + vec.z * mat.Row2.y; result.z = vec.x * mat.Row0.z + vec.y * mat.Row1.z + vec.z * mat.Row2.z; }
static public Frustum Transform(Frustum frustum, Matrix4X4 matrix) { Frustum transformedFrustum = new Frustum(); transformedFrustum.plane = new Plane[frustum.plane.Length]; for (int i = 0; i < frustum.plane.Length; ++i) { Vector3 planeNormal = frustum.plane[i].planeNormal; double distanceToPlane = frustum.plane[i].distanceToPlaneFromOrigin; transformedFrustum.plane[i].planeNormal = Vector3.TransformNormal(planeNormal, matrix); Vector3 pointOnPlane = planeNormal * distanceToPlane; Vector3 pointOnTransformedPlane = Vector3.TransformNormal(pointOnPlane, matrix); transformedFrustum.plane[i].distanceToPlaneFromOrigin = Vector3.Dot(transformedFrustum.plane[i].planeNormal, pointOnTransformedPlane); } return transformedFrustum; }
public void SetPrintLevelingEquation(Vector3 position0, Vector3 position1, Vector3 position2, Vector2 bedCenter) { if (position0 == position1 || position1 == position2 || position2 == position0) { return; } Plane planeOfPoints = new Plane(position0, position1, position2); Ray ray = new Ray(new Vector3(bedCenter, 0), Vector3.UnitZ); bool inFront; double distanceToPlaneAtBedCenter = planeOfPoints.GetDistanceToIntersection(ray, out inFront); Matrix4X4 makePointsFlatMatrix = Matrix4X4.CreateTranslation(-bedCenter.x, -bedCenter.y, -distanceToPlaneAtBedCenter); makePointsFlatMatrix *= Matrix4X4.CreateRotation(planeOfPoints.PlaneNormal, Vector3.UnitZ); makePointsFlatMatrix *= Matrix4X4.CreateTranslation(bedCenter.x, bedCenter.y, 0);//distanceToPlaneAtBedCenter); bedLevelMatrix = Matrix4X4.Invert(makePointsFlatMatrix); }
public AxisAlignedBoundingBox GetAxisAlignedBoundingBox(Matrix4X4 transform) { bool first = true; AxisAlignedBoundingBox totalBounds = null; foreach (Mesh mesh in Meshes) { AxisAlignedBoundingBox bounds = mesh.GetAxisAlignedBoundingBox(transform); if (first) { totalBounds = bounds; first = false; } else { totalBounds = totalBounds + bounds; } } return totalBounds; }
private void RenderLine(Matrix4X4 transform, Vector3 start, Vector3 end, RGBA_Bytes color, bool zBuffered = true) { Vector3 lineCenter = (start + end) / 2; Vector3 delta = start - end; Matrix4X4 rotateTransform = Matrix4X4.CreateRotation(new Quaternion(Vector3.UnitX + new Vector3(.0001, -.00001, .00002), delta.GetNormal())); Matrix4X4 scaleTransform = Matrix4X4.CreateScale((end - start).Length, 1, 1); Matrix4X4 lineTransform = scaleTransform * rotateTransform * Matrix4X4.CreateTranslation(lineCenter) * transform; if (zBuffered) { RenderMeshToGl.Render(lineMesh, RGBA_Bytes.Black, lineTransform, RenderTypes.Shaded); //drawEvent.graphics2D.Line(cornerPositionScreen, cornerPositionCcwScreen, RGBA_Bytes.Gray); } else { // render on top of everything very lightly RenderMeshToGl.Render(lineMesh, new RGBA_Bytes(RGBA_Bytes.Black, 5), lineTransform, RenderTypes.Shaded); } }
public string LookForNamedPartRecursive(Solid objectToProcess, Matrix4X4 accumulatedMatrix) { if (objectToProcess.Name == nameWeAreLookingFor) { Vector3 position = Vector3.TransformPosition(objectToProcess.GetCenter(), accumulatedMatrix); if (outputAsScad) { string output = "translate([" + position.x.ToString() + ", " + position.y.ToString() + ", " + position.z.ToString() + "])\n"; output += "sphere(1, $fn=10);\n"; return output; } else { Vector2 position2D = new Vector2(position.x, position.y); return position2D.x.ToString("0.000") + ", " + position2D.y.ToString("0.000") + "\n"; } } return ""; }
public override void OnMouseDown(MouseEventArgs mouseEvent) { // Show transform override if (activeButtonBeforeMouseOverride == null && mouseEvent.Button == MouseButtons.Right) { activeButtonBeforeMouseOverride = viewControls3D.ActiveButton; viewControls3D.ActiveButton = ViewControls3DButtons.Rotate; } else if (activeButtonBeforeMouseOverride == null && mouseEvent.Button == MouseButtons.Middle) { activeButtonBeforeMouseOverride = viewControls3D.ActiveButton; viewControls3D.ActiveButton = ViewControls3DButtons.Translate; } autoRotating = false; base.OnMouseDown(mouseEvent); if (meshViewerWidget.TrackballTumbleWidget.UnderMouseState == Agg.UI.UnderMouseState.FirstUnderMouse) { if (meshViewerWidget.TrackballTumbleWidget.TransformState == TrackBallController.MouseDownType.None && mouseEvent.Button == MouseButtons.Left && ModifierKeys != Keys.Shift && ModifierKeys != Keys.Control && ModifierKeys != Keys.Alt) { if (!meshViewerWidget.MouseDownOnInteractionVolume) { int meshGroupHitIndex; IntersectInfo info = new IntersectInfo(); if (FindMeshGroupHitPosition(mouseEvent.Position, out meshGroupHitIndex, ref info)) { CurrentSelectInfo.HitPlane = new PlaneShape(Vector3.UnitZ, CurrentSelectInfo.PlaneDownHitPos.z, null); SelectedMeshGroupIndex = meshGroupHitIndex; transformOnMouseDown = SelectedMeshGroupTransform; Invalidate(); CurrentSelectInfo.DownOnPart = true; AxisAlignedBoundingBox selectedBounds = meshViewerWidget.GetBoundsForSelection(); if (info.hitPosition.x < selectedBounds.Center.x) { if (info.hitPosition.y < selectedBounds.Center.y) { CurrentSelectInfo.HitQuadrant = HitQuadrant.LB; } else { CurrentSelectInfo.HitQuadrant = HitQuadrant.LT; } } else { if (info.hitPosition.y < selectedBounds.Center.y) { CurrentSelectInfo.HitQuadrant = HitQuadrant.RB; } else { CurrentSelectInfo.HitQuadrant = HitQuadrant.RT; } } } else { SelectedMeshGroupIndex = -1; } SelectedTransformChanged?.Invoke(this, null); } } } }
private void AdjustChildSize(object sender, EventArgs e) { if (Children.Count > 0) { var aabb = UntransformedChildren.GetAxisAlignedBoundingBox(); ItemWithTransform.Matrix = Matrix4X4.Identity; var scale = Vector3.One; if (StretchZ) { scale.Z = SizeZ / aabb.ZSize; } ItemWithTransform.Matrix = ItemWithTransform.Matrix.ApplyAtPosition(aabb.Center, Matrix4X4.CreateScale(scale)); Matrix4X4 centering; if (AlternateCentering) { centering = GetCenteringTransformVisualCenter(UntransformedChildren, Diameter / 2); } else { centering = GetCenteringTransformExpandedToRadius(UntransformedChildren, Diameter / 2); } ItemWithTransform.Matrix = ItemWithTransform.Matrix.ApplyAtPosition(aabb.Center, centering); } }
private void AddMirrorControls(FlowLayoutWidget buttonPanel) { List <GuiWidget> mirrorControls = new List <GuiWidget>(); double oldFixedWidth = view3DWidget.textImageButtonFactory.FixedWidth; view3DWidget.textImageButtonFactory.FixedWidth = view3DWidget.EditButtonHeight; FlowLayoutWidget buttonContainer = new FlowLayoutWidget(FlowDirection.LeftToRight); buttonContainer.HAnchor = HAnchor.ParentLeftRight; Button mirrorXButton = view3DWidget.textImageButtonFactory.Generate("X", centerText: true); buttonContainer.AddChild(mirrorXButton); mirrorControls.Add(mirrorXButton); mirrorXButton.Click += (object sender, EventArgs mouseEvent) => { if (view3DWidget.SelectedMeshGroupIndex != -1) { view3DWidget.SelectedMeshGroup.ReverseFaceEdges(); view3DWidget.SelectedMeshGroupTransform = PlatingHelper.ApplyAtCenter(view3DWidget.SelectedMeshGroup, view3DWidget.SelectedMeshGroupTransform, Matrix4X4.CreateScale(-1, 1, 1)); view3DWidget.PartHasBeenChanged(); Invalidate(); } }; Button mirrorYButton = view3DWidget.textImageButtonFactory.Generate("Y", centerText: true); buttonContainer.AddChild(mirrorYButton); mirrorControls.Add(mirrorYButton); mirrorYButton.Click += (object sender, EventArgs mouseEvent) => { if (view3DWidget.SelectedMeshGroupIndex != -1) { view3DWidget.SelectedMeshGroup.ReverseFaceEdges(); view3DWidget.SelectedMeshGroupTransform = PlatingHelper.ApplyAtCenter(view3DWidget.SelectedMeshGroup, view3DWidget.SelectedMeshGroupTransform, Matrix4X4.CreateScale(1, -1, 1)); view3DWidget.PartHasBeenChanged(); Invalidate(); } }; Button mirrorZButton = view3DWidget.textImageButtonFactory.Generate("Z", centerText: true); buttonContainer.AddChild(mirrorZButton); mirrorControls.Add(mirrorZButton); mirrorZButton.Click += (object sender, EventArgs mouseEvent) => { if (view3DWidget.SelectedMeshGroupIndex != -1) { view3DWidget.SelectedMeshGroup.ReverseFaceEdges(); view3DWidget.SelectedMeshGroupTransform = PlatingHelper.ApplyAtCenter(view3DWidget.SelectedMeshGroup, view3DWidget.SelectedMeshGroupTransform, Matrix4X4.CreateScale(1, 1, -1)); view3DWidget.PartHasBeenChanged(); Invalidate(); } }; buttonPanel.AddChild(buttonContainer); buttonPanel.AddChild(view3DWidget.GenerateHorizontalRule()); view3DWidget.textImageButtonFactory.FixedWidth = oldFixedWidth; }
public override Task Rebuild() { this.DebugDepth("Rebuild"); using (RebuildLock()) { using (new CenterAndHeightMaintainer(this)) { var path = new VertexStorage(); path.MoveTo(0, 0); path.LineTo(Math.Sqrt(2), 0); path.LineTo(0, Height); var mesh = VertexSourceToMesh.Revolve(path, 4); mesh.Transform(Matrix4X4.CreateRotationZ(MathHelper.DegreesToRadians(45)) * Matrix4X4.CreateScale(Width / 2, Depth / 2, 1)); Mesh = mesh; } } Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Mesh)); return(Task.CompletedTask); }
public static void Fit(this WorldView world, IObject3D itemToRender, RectangleDouble goalBounds, Matrix4X4 offset) { AxisAlignedBoundingBox meshBounds = itemToRender.GetAxisAlignedBoundingBox(offset); bool done = false; double scaleFraction = .1; // make the target size a portion of the total size goalBounds.Inflate(-goalBounds.Width * .1); int rescaleAttempts = 0; while (!done && rescaleAttempts++ < 500) { RectangleDouble partScreenBounds = GetScreenBounds(meshBounds, world); if (!NeedsToBeSmaller(partScreenBounds, goalBounds)) { world.Scale *= 1 + scaleFraction; partScreenBounds = GetScreenBounds(meshBounds, world); // If it crossed over the goal reduct the amount we are adjusting by. if (NeedsToBeSmaller(partScreenBounds, goalBounds)) { scaleFraction /= 2; } } else { world.Scale *= 1 - scaleFraction; partScreenBounds = GetScreenBounds(meshBounds, world); // If it crossed over the goal reduct the amount we are adjusting by. if (!NeedsToBeSmaller(partScreenBounds, goalBounds)) { scaleFraction /= 2; if (scaleFraction < .001) { done = true; } } } } }
public float[] MatrixRotation(Matrix4X4 X, float[] vertex) { float cosAa = Mathf.Cos(cosA * Mathf.Deg2Rad); float sinAa = Mathf.Sin(sinA * Mathf.Deg2Rad); float sinBb = Mathf.Sin(sinB * Mathf.Deg2Rad); float cosBb = Mathf.Cos(cosB * Mathf.Deg2Rad); // cos -sin 0 0 // sin cos 0 0 // 0 0 1 0 // 0 0 0 1 X.matrix[0, 0] = 1; X.matrix[1, 1] = 1; X.matrix[2, 2] = 1; X.matrix[3, 3] = 1; X.matrix[0, 3] = dx; X.matrix[1, 3] = dy; X.matrix[2, 3] = dz; Matrix4X4 X2 = new Matrix4X4(); X2.matrix[3, 3] = 1; X2.matrix[0, 0] = sx; X2.matrix[1, 1] = sy; X2.matrix[2, 2] = sz; Matrix4X4 X3 = new Matrix4X4(); for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { for (int k = 0; k < 4; k++) { X3.matrix[i, j] += X.matrix[i, k] * X2.matrix[k, j]; } } } Matrix4X4 X4 = new Matrix4X4(); X4.matrix[0, 0] = 1; X4.matrix[1, 1] = 1; X4.matrix[2, 2] = 1; X4.matrix[3, 3] = 1; X4.matrix[0, 3] = -1 * dx; X4.matrix[1, 3] = -1 * dy; X4.matrix[2, 3] = -1 * dz; Matrix4X4 X5 = new Matrix4X4(); for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { for (int k = 0; k < 4; k++) { X5.matrix[i, j] += X3.matrix[i, k] * X4.matrix[k, j]; } } } if (isRoll) { // cos -sin 0 0 // sin cos 0 0 // 0 0 1 0 // 0 0 0 1 X.matrix[2, 2] = 1; X.matrix[3, 3] = 1; X.matrix[0, 0] = cosAa; X.matrix[0, 1] = sinAa * (-1); X.matrix[1, 0] = sinBb; X.matrix[1, 1] = cosBb; } else if (ispitch) { // 1 0 0 0 // 0 cos -sin 0 // 0 sin cos 0 // 0 0 0 1 X.matrix[0, 0] = 1; X.matrix[3, 3] = 1; X.matrix[1, 1] = cosAa; X.matrix[1, 2] = sinAa * (-1); X.matrix[2, 1] = sinBb; X.matrix[2, 2] = cosBb; } else if (isYaw) { // cos 0 sin 0 // 0 1 0 0 // -sin 0 cos 0 // 0 0 0 1 X.matrix[1, 1] = 1; X.matrix[3, 3] = 1; X.matrix[0, 0] = cosAa; X.matrix[0, 2] = sinAa; X.matrix[2, 0] = sinBb * (-1); X.matrix[2, 2] = cosBb; } for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { X5.matrix[i, j] *= vertex[j]; } } float[] x = new float[4]; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { x[i] += X5.matrix[i, j]; } } return(x); }
private static bool CheckPosition(IEnumerable <IObject3D> itemsToAvoid, IObject3D itemToMove, AxisAlignedBoundingBox meshToMoveBounds, int yStep, int xStep, ref Matrix4X4 transform) { double xStepAmount = 5; double yStepAmount = 5; Matrix4X4 positionTransform = Matrix4X4.CreateTranslation(xStep * xStepAmount, yStep * yStepAmount, 0); Vector3 newPosition = Vector3Ex.Transform(Vector3.Zero, positionTransform); transform = Matrix4X4.CreateTranslation(newPosition); AxisAlignedBoundingBox testBounds = meshToMoveBounds.NewTransformed(transform); foreach (IObject3D meshToTest in itemsToAvoid) { if (meshToTest != itemToMove) { AxisAlignedBoundingBox existingMeshBounds = meshToTest.GetAxisAlignedBoundingBox(); AxisAlignedBoundingBox intersection = AxisAlignedBoundingBox.Intersection(testBounds, existingMeshBounds); if (intersection.XSize > 0 && intersection.YSize > 0) { return(false); } } } return(true); }
static public void CreateCylinder(VectorPOD <ColorVertexData> colorVertexData, VectorPOD <int> indexData, Vector3 startPos, Vector3 endPos, double radius, int steps, Color color, double layerHeight) { Vector3 direction = endPos - startPos; Vector3 directionNormal = direction.GetNormal(); Vector3 startSweepDirection = Vector3.GetPerpendicular(startPos, endPos).GetNormal(); int[] tubeStartIndices = new int[steps]; int[] tubeEndIndices = new int[steps]; int[] capStartIndices = new int[steps]; int[] capEndIndices = new int[steps]; double halfHeight = layerHeight / 2 + (layerHeight * .1); double halfWidth = radius; double zScale = halfHeight / radius; double xScale = halfWidth / radius; // Adjust start/end positions to be centered on Z for the given layer height startPos.Z -= halfHeight; endPos.Z -= halfHeight; Vector3 scale = new Vector3(xScale, xScale, zScale); for (int i = 0; i < steps; i++) { // create tube ends verts Vector3 tubeNormal = Vector3.Transform(startSweepDirection, Matrix4X4.CreateRotation(direction, MathHelper.Tau / (steps * 2) + MathHelper.Tau / (steps) * i)); Vector3 offset = Vector3.Transform(startSweepDirection * radius, Matrix4X4.CreateRotation(direction, MathHelper.Tau / (steps * 2) + MathHelper.Tau / (steps) * i)); offset *= scale; Vector3 tubeStart = startPos + offset; tubeStartIndices[i] = colorVertexData.Count; colorVertexData.Add(new ColorVertexData(tubeStart, tubeNormal, color)); Vector3 tubeEnd = endPos + offset; tubeEndIndices[i] = colorVertexData.Count; colorVertexData.Add(new ColorVertexData(tubeEnd, tubeNormal, color)); // create cap verts Vector3 rotateAngle = Vector3.Cross(startSweepDirection, direction); Vector3 capStartNormal = Vector3.Transform(startSweepDirection, Matrix4X4.CreateRotation(rotateAngle, MathHelper.Tau / 8)); capStartNormal = Vector3.Transform(capStartNormal, Matrix4X4.CreateRotation(direction, MathHelper.Tau / (steps * 2) + MathHelper.Tau / (steps) * i)); capStartNormal = (capStartNormal * scale).GetNormal(); Vector3 capStartOffset = capStartNormal * radius; capStartOffset *= scale; Vector3 capStart = startPos + capStartOffset; capStartIndices[i] = colorVertexData.Count; colorVertexData.Add(new ColorVertexData(capStart, capStartNormal, color)); Vector3 capEndNormal = Vector3.Transform(startSweepDirection, Matrix4X4.CreateRotation(-rotateAngle, MathHelper.Tau / 8)); capEndNormal = Vector3.Transform(capEndNormal, Matrix4X4.CreateRotation(direction, MathHelper.Tau / (steps * 2) + MathHelper.Tau / (steps) * i)); capEndNormal = (capEndNormal * scale).GetNormal(); Vector3 capEndOffset = capEndNormal * radius; capEndOffset *= scale; Vector3 capEnd = endPos + capEndOffset; capEndIndices[i] = colorVertexData.Count; colorVertexData.Add(new ColorVertexData(capEnd, capEndNormal, color)); } int tipStartIndex = colorVertexData.Count; Vector3 tipOffset = directionNormal * radius; tipOffset *= scale; colorVertexData.Add(new ColorVertexData(startPos - tipOffset, -directionNormal, color)); int tipEndIndex = colorVertexData.Count; colorVertexData.Add(new ColorVertexData(endPos + tipOffset, directionNormal, color)); for (int i = 0; i < steps; i++) { // create tube polys indexData.Add(tubeStartIndices[i]); indexData.Add(tubeEndIndices[i]); indexData.Add(tubeEndIndices[(i + 1) % steps]); indexData.Add(tubeStartIndices[i]); indexData.Add(tubeEndIndices[(i + 1) % steps]); indexData.Add(tubeStartIndices[(i + 1) % steps]); // create start cap polys indexData.Add(tubeStartIndices[i]); indexData.Add(capStartIndices[i]); indexData.Add(capStartIndices[(i + 1) % steps]); indexData.Add(tubeStartIndices[i]); indexData.Add(capStartIndices[(i + 1) % steps]); indexData.Add(tubeStartIndices[(i + 1) % steps]); // create end cap polys indexData.Add(tubeEndIndices[i]); indexData.Add(capEndIndices[i]); indexData.Add(capEndIndices[(i + 1) % steps]); indexData.Add(tubeEndIndices[i]); indexData.Add(capEndIndices[(i + 1) % steps]); indexData.Add(tubeEndIndices[(i + 1) % steps]); // create start tip polys indexData.Add(tipStartIndex); indexData.Add(capStartIndices[i]); indexData.Add(capStartIndices[(i + 1) % steps]); // create end tip polys indexData.Add(tipEndIndex); indexData.Add(capEndIndices[i]); indexData.Add(capEndIndices[(i + 1) % steps]); } }
override public Task Rebuild() { this.DebugDepth("Rebuild"); bool valuesChanged = Diameter == double.MinValue; if (StartPercent < 0 || StartPercent > 100) { StartPercent = Math.Min(100, Math.Max(0, StartPercent)); valuesChanged = true; } using (RebuildLock()) { ResetMeshWrapperMeshes(Object3DPropertyFlags.All, CancellationToken.None); // remember the current matrix then clear it so the parts will rotate at the original wrapped position var currentMatrix = Matrix; Matrix = Matrix4X4.Identity; var meshWrapperEnumerator = WrappedObjects(); var aabb = this.GetAxisAlignedBoundingBox(); if (Diameter == double.MinValue) { // uninitialized set to a reasonable value Diameter = (int)aabb.XSize; // TODO: ensure that the editor display value is updated } if (Diameter > 0) { var radius = Diameter / 2; var circumference = MathHelper.Tau * radius; rotationCenter = new Vector2(aabb.MinXYZ.X + (aabb.MaxXYZ.X - aabb.MinXYZ.X) * (StartPercent / 100), aabb.MaxXYZ.Y + radius); foreach (var object3Ds in meshWrapperEnumerator) { var matrix = object3Ds.original.WorldMatrix(this); if (!BendCcw) { // rotate around so it will bend correctly matrix *= Matrix4X4.CreateTranslation(0, -aabb.MaxXYZ.Y, 0); matrix *= Matrix4X4.CreateRotationX(MathHelper.Tau / 2); matrix *= Matrix4X4.CreateTranslation(0, aabb.MaxXYZ.Y - aabb.YSize, 0); } var matrixInv = matrix.Inverted; var curvedMesh = object3Ds.meshCopy.Mesh; // split long edges so it will be curved if (false) { double numRotations = aabb.XSize / circumference; double numberOfCuts = numRotations * MinSidesPerRotation; var maxXLength = aabb.XSize / numberOfCuts; var maxXLengthSqrd = maxXLength * maxXLength; // convert the mesh into vertex and face arrays double[] v; int[] f; v = curvedMesh.Vertices.ToDoubleArray(object3Ds.meshCopy.Matrix); f = curvedMesh.Faces.ToIntArray(); // make lists so we can add to them var vL = v.ToVector3List(); vL.Transform(matrix); var fL = new FaceList(f, curvedMesh.Vertices); Teselate.SplitEdges(vL, fL, maxXLength); vL.Transform(matrixInv); // convert the lists back into the mesh object3Ds.meshCopy.Mesh = new Mesh(vL, fL); curvedMesh = object3Ds.meshCopy.Mesh; } for (int i = 0; i < curvedMesh.Vertices.Count; i++) { var worldPosition = curvedMesh.Vertices[i].Transform((Matrix4X4)matrix); var angleToRotate = ((worldPosition.X - rotationCenter.X) / circumference) * MathHelper.Tau - MathHelper.Tau / 4; var distanceFromCenter = rotationCenter.Y - worldPosition.Y; var rotatePosition = new Vector3Float(Math.Cos(angleToRotate), Math.Sin(angleToRotate), 0) * distanceFromCenter; rotatePosition.Z = worldPosition.Z; var worldWithBend = rotatePosition + new Vector3Float(rotationCenter.X, radius + aabb.MaxXYZ.Y, 0); curvedMesh.Vertices[i] = worldWithBend.Transform(matrixInv); } curvedMesh.MarkAsChanged(); curvedMesh.CalculateNormals(); } if (!BendCcw) { // fix the stored center so we draw correctly rotationCenter = new Vector2(rotationCenter.X, aabb.MinXYZ.Y - radius); } } // set the matrix back Matrix = currentMatrix; } Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Mesh)); if (valuesChanged) { Invalidate(InvalidateType.DisplayValues); } return(Task.CompletedTask); }
public static Matrix4X4 ApplyAtPosition(this Matrix4X4 currentTransform, Vector3 positionToApplyAt, Matrix4X4 transformToApply) { currentTransform *= Matrix4X4.CreateTranslation(-positionToApplyAt); currentTransform *= transformToApply; currentTransform *= Matrix4X4.CreateTranslation(positionToApplyAt); return currentTransform; }
public static Matrix4X4 ApplyAtPosition(this IObject3D item, Vector3 positionToApplyAt, Matrix4X4 transformToApply) { return item.Matrix.ApplyAtPosition(positionToApplyAt, transformToApply); }
public static Matrix4X4 ApplyAtCenter(this Matrix4X4 currentTransform, AxisAlignedBoundingBox boundsToApplyTo, Matrix4X4 transformToApply) { return ApplyAtPosition(currentTransform, boundsToApplyTo.Center, transformToApply); }
public static Matrix4X4 ApplyAtBoundsCenter(this IObject3D objectWithBounds, Matrix4X4 transformToApply) { return ApplyAtCenter(objectWithBounds.Matrix, objectWithBounds.GetAxisAlignedBoundingBox(Matrix4X4.Identity), transformToApply); }
public static void Render(Mesh meshToRender, IColorType partColor, Matrix4X4 transform, RenderTypes renderType) { if (meshToRender != null) { GL.Color4(partColor.Red0To255, partColor.Green0To255, partColor.Blue0To255, partColor.Alpha0To255); if (partColor.Alpha0To1 < 1) { GL.Enable(EnableCap.Blend); } else { GL.Disable(EnableCap.Blend); } GL.MatrixMode(MatrixMode.Modelview); GL.PushMatrix(); GL.MultMatrix(transform.GetAsFloatArray()); switch (renderType) { case RenderTypes.Hidden: break; case RenderTypes.Shaded: DrawToGL(meshToRender); break; case RenderTypes.Polygons: case RenderTypes.Outlines: DrawWithWireOverlay(meshToRender, renderType); break; } GL.PopMatrix(); } }
public static IObject3D Scale(this IObject3D objectToTranslate, Vector3 translation, string name = "") { objectToTranslate.Matrix *= Matrix4X4.CreateScale(translation); return objectToTranslate; }
public override void OnMouseMove(Mouse3DEventArgs mouseEvent3D, bool mouseIsOver) { var selectedItem = RootSelection; activeSelectedItem = selectedItem; if (MouseIsOver) { zValueDisplayInfo.Visible = true; } else if (!hadClickOnControl) { zValueDisplayInfo.Visible = false; } if (MouseDownOnControl) { IntersectInfo info = hitPlane.GetClosestIntersection(mouseEvent3D.MouseRay); if (info != null && selectedItem != null) { AxisAlignedBoundingBox originalSelectedBounds = selectedItem.GetAxisAlignedBoundingBox(); Vector3 delta = info.HitPosition - initialHitPosition; Vector3 newPosition = originalPointToMove + delta; if (Object3DControlContext.SnapGridDistance > 0) { // snap this position to the grid double snapGridDistance = Object3DControlContext.SnapGridDistance; // snap this position to the grid newPosition.Z = ((int)((newPosition.Z / snapGridDistance) + .5)) * snapGridDistance; } Vector3 topPosition = GetTopPosition(selectedItem); var lockedBottom = new Vector3(topPosition.X, topPosition.Y, originalSelectedBounds.MinXYZ.Z); Vector3 newSize = Vector3.Zero; newSize.Z = newPosition.Z - lockedBottom.Z; // scale it Vector3 scaleAmount = ScaleCornerControl.GetScalingConsideringShiftKey(originalSelectedBounds, mouseDownSelectedBounds, newSize, Object3DControlContext.GuiSurface.ModifierKeys); var scale = Matrix4X4.CreateScale(scaleAmount); selectedItem.Matrix = selectedItem.ApplyAtBoundsCenter(scale); // and keep the locked edge in place AxisAlignedBoundingBox scaledSelectedBounds = selectedItem.GetAxisAlignedBoundingBox(); var newLockedBottom = new Vector3(topPosition.X, topPosition.Y, scaledSelectedBounds.MinXYZ.Z); selectedItem.Matrix *= Matrix4X4.CreateTranslation(lockedBottom - newLockedBottom); Invalidate(); } } base.OnMouseMove(mouseEvent3D, mouseIsOver); }
private (Vector3 edge, Vector3 otherSide) GetHitPosition(IObject3D selectedItem) { Vector3 GetEdgePosition(IObject3D item, double angle, ObjectSpace.Placement placement) { var aabb = item.GetAxisAlignedBoundingBox(item.Matrix.Inverted); var centerPosition = aabb.Center; switch (placement) { case ObjectSpace.Placement.Bottom: centerPosition.Z = aabb.MinXYZ.Z; break; case ObjectSpace.Placement.Center: centerPosition.Z = aabb.Center.Z; break; case ObjectSpace.Placement.Top: centerPosition.Z = aabb.MaxXYZ.Z; break; } var offset = new Vector3(getDiameters[diameterIndex]() / 2, 0, 0).Transform(Matrix4X4.CreateRotationZ(angle + angleOffset)); centerPosition += offset; return(centerPosition.Transform(item.Matrix)); } var bestZEdgePosition = Vector3.Zero; var otherSide = Vector3.Zero; var bestCornerZ = double.PositiveInfinity; // get the closest z on the bottom in view space var rotations = 16; for (int i = 0; i < rotations; i++) { Vector3 cornerPosition = GetEdgePosition(selectedItem, MathHelper.Tau * i / rotations, placement); Vector3 cornerScreenSpace = Object3DControlContext.World.WorldToScreenSpace(cornerPosition); if (cornerScreenSpace.Z < bestCornerZ) { bestCornerZ = cornerScreenSpace.Z; bestZEdgePosition = cornerPosition; otherSide = GetEdgePosition(selectedItem, MathHelper.Tau * i / rotations + MathHelper.Tau / 2, placement); } } return(bestZEdgePosition, otherSide); }
public override void OnMouseDown(MouseEvent3DArgs mouseEvent3D) { zHitHeight = mouseEvent3D.info.hitPosition.z; lastMoveDelta = new Vector3(); double distanceToHit = Vector3.Dot(mouseEvent3D.info.hitPosition, mouseEvent3D.MouseRay.directionNormal); hitPlane = new PlaneShape(mouseEvent3D.MouseRay.directionNormal, distanceToHit, null); IntersectInfo info = hitPlane.GetClosestIntersection(mouseEvent3D.MouseRay); zHitHeight = info.hitPosition.z; transformOnMouseDown = MeshViewerToDrawWith.SelectedMeshGroupTransform; base.OnMouseDown(mouseEvent3D); }
/// <summary> /// Create a bounding volume hierarchy for the give mesh. /// </summary> /// <param name="mesh">The mesh to add the BVH to.</param> /// <param name="material">The tracing material to use.</param> /// <param name="matrix">A transformation to apply to the trace data</param> /// <param name="maxRecursion">The max depth to create the BVH tree.</param> /// <returns>The created BVH tree.</returns> public static IPrimitive CreateBVHData(this Mesh mesh, MaterialAbstract material, Matrix4X4 matrix, int maxRecursion = int.MaxValue) { var allPolys = new List <IPrimitive>(); mesh.AddTracePrimitives(material, matrix, allPolys); return(BoundingVolumeHierarchy.CreateNewHierachy(allPolys, maxRecursion)); }
//Mouse drag, calculate rotation public void OnMouseMove(Vector2 mousePosition) { switch (currentTrackingType) { case MouseDownType.Rotation: activeRotationQuaternion = Quaternion.Identity; //Map the point to the sphere MapToSphere(mousePosition, out rotationCurrent); //Return the quaternion equivalent to the rotation //Compute the vector perpendicular to the begin and end vectors Vector3 Perp = Vector3.Cross(rotationStart, rotationCurrent); //Compute the length of the perpendicular vector if (Perp.Length > Epsilon) { //if its non-zero //We're ok, so return the perpendicular vector as the transform after all activeRotationQuaternion.X = Perp.x; activeRotationQuaternion.Y = Perp.y; activeRotationQuaternion.Z = Perp.z; //In the quaternion values, w is cosine (theta / 2), where theta is the rotation angle activeRotationQuaternion.W = Vector3.Dot(rotationStart, rotationCurrent); OnTransformChanged(null); } break; case MouseDownType.Translation: { Vector2 mouseDelta = mousePosition - lastTranslationMousePosition; Vector2 scaledDelta = mouseDelta / screenCenter.x * 4.75; Vector3 offset = new Vector3(scaledDelta.x, scaledDelta.y, 0); offset = Vector3.TransformPosition(offset, Matrix4X4.Invert(CurrentRotation)); offset = Vector3.TransformPosition(offset, localToScreenTransform); currentTranslationMatrix = currentTranslationMatrix * Matrix4X4.CreateTranslation(offset); lastTranslationMousePosition = mousePosition; OnTransformChanged(null); } break; case MouseDownType.Scale: { Vector2 mouseDelta = mousePosition - lastScaleMousePosition; double zoomDelta = 1; if (mouseDelta.y < 0) { zoomDelta = 1 - (-1 * mouseDelta.y / 100); } else if (mouseDelta.y > 0) { zoomDelta = 1 + (1 * mouseDelta.y / 100); } currentTranslationMatrix *= Matrix4X4.CreateScale(zoomDelta); lastScaleMousePosition = mousePosition; OnTransformChanged(null); } break; default: throw new NotImplementedException(); } }
public override Task Rebuild() { // Point Size 10 // Height 1 // Font Fredoka // Align // X: Right Right -11 // Y: Front -.3 // Z: Bottom .8 this.DebugDepth("Rebuild"); bool valuesChanged = false; using (RebuildLock()) { MaxTemperature = agg_basics.Clamp(MaxTemperature, 140, 400, ref valuesChanged); Sections = agg_basics.Clamp(Sections, 2, 20, ref valuesChanged); ChangeAmount = agg_basics.Clamp(ChangeAmount, 1, 30, ref valuesChanged); using (new CenterAndHeightMaintainer(this)) { Children.Modify(async(children) => { children.Clear(); // add the base var towerBase = new Object3D() { Mesh = new RoundedRect(-25, -15, 25, 15, 3) { ResolutionScale = 10 }.Extrude(BaseHeight), Name = "Base" }; children.Add(towerBase); // Add each section for (int i = 0; i < Sections; i++) { var temp = MaxTemperature - i * ChangeAmount; var section = new Object3D() { Matrix = Matrix4X4.CreateTranslation(0, 0, BaseHeight + i * SectionHeight), Name = $"{temp:0.##}" }; children.Add(section); // Add base mesh section.Children.Add(new Object3D() { Mesh = shape, Name = "CC - gaaZolee - AS" }); // Add temp changer section.Children.Add(new SetTemperatureObject3D() { Temperature = temp, Name = $"Set to {temp:0.##}", Matrix = Matrix4X4.CreateScale(.2, .1, 1) }); // Add temperature text var text = new TextObject3D() { Font = NamedTypeFace.Fredoka, Height = 1, Name = $"{temp:0.##}", PointSize = 10, NameToWrite = $"{temp:0.##}", Matrix = Matrix4X4.CreateRotationX(MathHelper.Tau / 4) * Matrix4X4.CreateTranslation(0, -4.3, .8), }; text.Rebuild().Wait(); var textBounds = text.GetAxisAlignedBoundingBox(); text.Matrix *= Matrix4X4.CreateTranslation(11 - textBounds.MaxXYZ.X, 0, 0); section.Children.Add(text); } }); } } Invalidate(InvalidateType.DisplayValues); Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Mesh)); return(Task.CompletedTask); }
public void OnMouseWheel(int wheelDelta) { double zoomDelta = 1; if (wheelDelta > 0) { zoomDelta = 1.2; } else if (wheelDelta < 0) { zoomDelta = .8; } currentTranslationMatrix *= Matrix4X4.CreateScale(zoomDelta); OnTransformChanged(null); }
public void SubtractIcosahedronsWorks() { Vector3 centering = new Vector3(100, 100, 20); Mesh meshA = PlatonicSolids.CreateIcosahedron(35); meshA.Translate(centering); Mesh meshB = PlatonicSolids.CreateIcosahedron(35); Vector3 finalTransform = new Vector3(105.240172225344, 92.9716306394062, 18.4619570261172); Vector3 rotCurrent = new Vector3(4.56890223673623, -2.67874102322035, 1.02768848238523); Vector3 scaleCurrent = new Vector3(1.07853517569753, 0.964980885267323, 1.09290934544604); Matrix4X4 transformB = Matrix4X4.CreateScale(scaleCurrent) * Matrix4X4.CreateRotation(rotCurrent) * Matrix4X4.CreateTranslation(finalTransform); meshB.Transform(transformB); Mesh meshToAdd = CsgOperations.Subtract(meshA, meshB); AxisAlignedBoundingBox a_aabb = meshA.GetAxisAlignedBoundingBox(); AxisAlignedBoundingBox b_aabb = meshB.GetAxisAlignedBoundingBox(); AxisAlignedBoundingBox intersect_aabb = meshToAdd.GetAxisAlignedBoundingBox(); Assert.IsTrue(a_aabb.XSize == 40 && a_aabb.YSize == 40 && a_aabb.ZSize == 40); Assert.IsTrue(intersect_aabb.XSize == 40 && intersect_aabb.YSize == 40 && intersect_aabb.ZSize == 40); }
public static void AddTracePrimitives(this Mesh mesh, MaterialAbstract material, Matrix4X4 matrix, List <IPrimitive> tracePrimitives) { for (int faceIndex = 0; faceIndex < mesh.Faces.Count; faceIndex++) { var face = mesh.Faces[faceIndex]; IPrimitive triangle; if (material != null) { triangle = new TriangleShape( mesh.Vertices[face.v0].Transform(matrix), mesh.Vertices[face.v1].Transform(matrix), mesh.Vertices[face.v2].Transform(matrix), material); } else { triangle = new MinimalTriangle((fi, vi) => { switch (vi) { case 0: return(mesh.Vertices[mesh.Faces[fi].v0]); case 1: return(mesh.Vertices[mesh.Faces[fi].v1]); default: return(mesh.Vertices[mesh.Faces[fi].v2]); } }, faceIndex); } tracePrimitives.Add(triangle); } }
public static IObject3D Translate(this IObject3D objectToTranslate, Vector3 translation, string name = "") { objectToTranslate.Matrix *= Matrix4X4.CreateTranslation(translation); return(objectToTranslate); }
public static BspNode RenderOrder(this BspNode node, List <Face> meshFaces, Matrix4X4 meshToViewTransform, Matrix4X4 invMeshToViewTransform) { var faceNormalInViewSpace = Vector3.TransformNormalInverse(meshFaces[node.Index].Normal, invMeshToViewTransform); var pointOnFaceInViewSpace = Vector3.Transform(meshFaces[node.Index].firstFaceEdge.FirstVertex.Position, meshToViewTransform); var infrontOfFace = Vector3.Dot(faceNormalInViewSpace, pointOnFaceInViewSpace) < 0; if (infrontOfFace) { return(new BspNode() { Index = node.Index, BackNode = node.BackNode, FrontNode = node.FrontNode }); } else { return(new BspNode() { Index = node.Index, BackNode = node.FrontNode, FrontNode = node.BackNode }); } }
private void CreateBooleanTestGeometry(GuiWidget drawingWidget, DrawEventArgs e) { try { booleanGroup = new MeshGroup(); booleanGroup.Meshes.Add(ApplyBoolean(PolygonMesh.Csg.CsgOperations.Union, AxisAlignedBoundingBox.Union, new Vector3(100, 0, 20), "U")); booleanGroup.Meshes.Add(ApplyBoolean(PolygonMesh.Csg.CsgOperations.Subtract, null, new Vector3(100, 100, 20), "S")); booleanGroup.Meshes.Add(ApplyBoolean(PolygonMesh.Csg.CsgOperations.Intersect, AxisAlignedBoundingBox.Intersection , new Vector3(100, 200, 20), "I")); offset += direction; rotCurrent += rotChange; scaleCurrent += scaleChange; meshViewerWidget.MeshGroups.Add(booleanGroup); groupTransform = Matrix4X4.Identity; meshViewerWidget.MeshGroupTransforms.Add(groupTransform); } catch(Exception e2) { string text = e2.Message; int a = 0; } }
/// <summary> /// Get an ordered list of the faces to render based on the camera position. /// </summary> /// <param name="node"></param> /// <param name="meshToViewTransform"></param> /// <param name="invMeshToViewTransform"></param> /// <param name="faceRenderOrder"></param> public static IEnumerable <Face> GetFacesInVisibiltyOrder(List <Face> meshFaces, BspNode root, Matrix4X4 meshToViewTransform, Matrix4X4 invMeshToViewTransform) { var renderOrder = new Stack <BspNode>(new BspNode[] { root.RenderOrder(meshFaces, meshToViewTransform, invMeshToViewTransform) }); do { var lastBack = renderOrder.Peek().BackNode; while (lastBack != null && lastBack.Index != -1) { renderOrder.Peek().BackNode = null; renderOrder.Push(lastBack.RenderOrder(meshFaces, meshToViewTransform, invMeshToViewTransform)); lastBack = renderOrder.Peek().BackNode; } var node = renderOrder.Pop(); if (node.Index != -1) { yield return(meshFaces[node.Index]); } var lastFront = node.FrontNode; if (lastFront != null && lastFront.Index != -1) { renderOrder.Push(lastFront.RenderOrder(meshFaces, meshToViewTransform, invMeshToViewTransform)); } } while (renderOrder.Any()); }
public static void Render3DLineNoPrep(this WorldView world, Frustum clippingFrustum, Vector3 start, Vector3 end, Color color, double width = 1) { if (clippingFrustum.ClipLine(ref start, ref end)) { double unitsPerPixelStart = world.GetWorldUnitsPerScreenPixelAtPosition(start); double unitsPerPixelEnd = world.GetWorldUnitsPerScreenPixelAtPosition(end); Vector3 delta = start - end; var deltaLength = delta.Length; var rotateTransform = Matrix4X4.CreateRotation(new Quaternion(Vector3.UnitX + new Vector3(.0001, -.00001, .00002), -delta / deltaLength)); var scaleTransform = Matrix4X4.CreateScale(deltaLength, 1, 1); Vector3 lineCenter = (start + end) / 2; Matrix4X4 lineTransform = scaleTransform * rotateTransform * Matrix4X4.CreateTranslation(lineCenter); var startScale = unitsPerPixelStart * width; var endScale = unitsPerPixelEnd * width; for (int i = 0; i < unscaledLineMesh.Vertices.Count; i++) { var vertexPosition = unscaledLineMesh.Vertices[i]; if (vertexPosition.X < 0) { scaledLineMesh.Vertices[i] = new Vector3Float(vertexPosition.X, vertexPosition.Y * startScale, vertexPosition.Z * startScale); } else { scaledLineMesh.Vertices[i] = new Vector3Float(vertexPosition.X, vertexPosition.Y * endScale, vertexPosition.Z * endScale); } } if (true) { GL.Color4(color.Red0To255, color.Green0To255, color.Blue0To255, color.Alpha0To255); if (color.Alpha0To1 < 1) { GL.Enable(EnableCap.Blend); } else { // GL.Disable(EnableCap.Blend); } GL.MatrixMode(MatrixMode.Modelview); GL.PushMatrix(); GL.MultMatrix(lineTransform.GetAsFloatArray()); GL.Begin(BeginMode.Triangles); for (int faceIndex = 0; faceIndex < scaledLineMesh.Faces.Count; faceIndex++) { var face = scaledLineMesh.Faces[faceIndex]; var vertices = scaledLineMesh.Vertices; var position = vertices[face.v0]; GL.Vertex3(position.X, position.Y, position.Z); position = vertices[face.v1]; GL.Vertex3(position.X, position.Y, position.Z); position = vertices[face.v2]; GL.Vertex3(position.X, position.Y, position.Z); } GL.End(); GL.PopMatrix(); } else { scaledLineMesh.MarkAsChanged(); GLHelper.Render(scaledLineMesh, color, lineTransform, RenderTypes.Shaded); } } }
// Getting the Martix for the Mesh public Matrix4X4 GetWorldMatrix() { return(Matrix4X4.Trs(Position, Rotation, Scale)); }
public ScaleTopControl(IObject3DControlContext context) : base(context) { theme = AppContext.Theme; zValueDisplayInfo = new InlineEditControl() { ForceHide = () => { // if the selection changes if (RootSelection != activeSelectedItem) { return(true); } // if another control gets a hover if (Object3DControlContext.HoveredObject3DControl != this && Object3DControlContext.HoveredObject3DControl != null) { return(true); } // if we clicked on the control if (hadClickOnControl) { return(false); } return(false); }, GetDisplayString = (value) => "{0:0.0}".FormatWith(value) }; zValueDisplayInfo.VisibleChanged += (s, e) => { if (!zValueDisplayInfo.Visible) { hadClickOnControl = false; } }; zValueDisplayInfo.EditComplete += (s, e) => { var selectedItem = activeSelectedItem; Matrix4X4 startingTransform = selectedItem.Matrix; var originalSelectedBounds = selectedItem.GetAxisAlignedBoundingBox(); Vector3 topPosition = GetTopPosition(selectedItem); var lockedBottom = new Vector3(topPosition.X, topPosition.Y, originalSelectedBounds.MinXYZ.Z); Vector3 newSize = Vector3.Zero; newSize.Z = zValueDisplayInfo.Value; Vector3 scaleAmount = ScaleCornerControl.GetScalingConsideringShiftKey(originalSelectedBounds, mouseDownSelectedBounds, newSize, Object3DControlContext.GuiSurface.ModifierKeys); var scale = Matrix4X4.CreateScale(scaleAmount); selectedItem.Matrix = selectedItem.ApplyAtBoundsCenter(scale); // and keep the locked edge in place AxisAlignedBoundingBox scaledSelectedBounds = selectedItem.GetAxisAlignedBoundingBox(); var newLockedBottom = new Vector3(topPosition.X, topPosition.Y, scaledSelectedBounds.MinXYZ.Z); selectedItem.Matrix *= Matrix4X4.CreateTranslation(lockedBottom - newLockedBottom); Invalidate(); Object3DControlContext.Scene.AddTransformSnapshot(startingTransform); }; Object3DControlContext.GuiSurface.AddChild(zValueDisplayInfo); DrawOnTop = true; topScaleMesh = PlatonicSolids.CreateCube(arrowSize, arrowSize, arrowSize); CollisionVolume = topScaleMesh.CreateBVHData(); Object3DControlContext.GuiSurface.BeforeDraw += Object3DControl_BeforeDraw; }
public void Draw(GuiWidget sender, IObject3D item, bool isSelected, DrawEventArgs e, Matrix4X4 itemMaxtrix, WorldView world) { if (isSelected && scene.DrawSelection) { var selectionColor = Color.White; double secondsSinceSelectionChanged = (UiThread.CurrentTimerMs - lastSelectionChangedMs) / 1000.0; if (secondsSinceSelectionChanged < .5) { var accentColor = Color.LightGray; if (secondsSinceSelectionChanged < .25) { selectionColor = Color.White.Blend(accentColor, Quadratic.InOut(secondsSinceSelectionChanged * 4)); } else { selectionColor = accentColor.Blend(Color.White, Quadratic.InOut((secondsSinceSelectionChanged - .25) * 4)); } guiWidget.Invalidate(); } if (item.Color != Color.Transparent) { selectionColor = selectionColor.WithAlpha(item.Color.Alpha0To255); } this.RenderSelection(item, selectionColor, world); } }
//Mouse down public void OnMouseDown(Vector2 mousePosition, Matrix4X4 screenToLocal, MouseDownType trackType = MouseDownType.Rotation) { //if (currentTrackingType == MouseDownType.None) { localToScreenTransform = Matrix4X4.Invert(screenToLocal); currentTrackingType = trackType; switch (currentTrackingType) { case MouseDownType.Rotation: MapToSphere(mousePosition, out rotationStart); break; case MouseDownType.Translation: lastTranslationMousePosition = mousePosition; break; case MouseDownType.Scale: lastScaleMousePosition = mousePosition; break; default: throw new NotImplementedException(); } } }
private void ArrangeMeshGroups() { Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; PushMeshGroupDataToAsynchLists(TraceInfoOpperation.DONT_COPY); // move them all out of the way for (int i = 0; i < asynchMeshGroups.Count; i++) { ScaleRotateTranslate translate = asynchMeshGroupTransforms[i]; translate.translation *= Matrix4X4.CreateTranslation(10000, 10000, 0); asynchMeshGroupTransforms[i] = translate; } // sort them by size for (int i = 0; i < asynchMeshGroups.Count; i++) { AxisAlignedBoundingBox iAABB = asynchMeshGroups[i].GetAxisAlignedBoundingBox(asynchMeshGroupTransforms[i].TotalTransform); for (int j = i + 1; j < asynchMeshGroups.Count; j++) { AxisAlignedBoundingBox jAABB = asynchMeshGroups[j].GetAxisAlignedBoundingBox(asynchMeshGroupTransforms[j].TotalTransform); if (Math.Max(iAABB.XSize, iAABB.YSize) < Math.Max(jAABB.XSize, jAABB.YSize)) { PlatingMeshGroupData tempData = asynchPlatingDatas[i]; asynchPlatingDatas[i] = asynchPlatingDatas[j]; asynchPlatingDatas[j] = tempData; MeshGroup tempMeshGroup = asynchMeshGroups[i]; asynchMeshGroups[i] = asynchMeshGroups[j]; asynchMeshGroups[j] = tempMeshGroup; ScaleRotateTranslate iTransform = asynchMeshGroupTransforms[i]; ScaleRotateTranslate jTransform = asynchMeshGroupTransforms[j]; Matrix4X4 tempTransform = iTransform.translation; iTransform.translation = jTransform.translation; jTransform.translation = tempTransform; asynchMeshGroupTransforms[i] = jTransform; asynchMeshGroupTransforms[j] = iTransform; iAABB = jAABB; } } } double ratioPerMeshGroup = 1.0 / asynchMeshGroups.Count; double currentRatioDone = 0; // put them onto the plate (try the center) starting with the biggest and moving down for (int meshGroupIndex = 0; meshGroupIndex < asynchMeshGroups.Count; meshGroupIndex++) { bool continueProcessing2 = true; ReportProgressChanged(currentRatioDone, "Calculating Positions...".Localize(), out continueProcessing2); MeshGroup meshGroup = asynchMeshGroups[meshGroupIndex]; Vector3 meshLowerLeft = meshGroup.GetAxisAlignedBoundingBox(asynchMeshGroupTransforms[meshGroupIndex].TotalTransform).minXYZ; ScaleRotateTranslate atZero = asynchMeshGroupTransforms[meshGroupIndex]; atZero.translation *= Matrix4X4.CreateTranslation(-meshLowerLeft); asynchMeshGroupTransforms[meshGroupIndex] = atZero; PlatingHelper.MoveMeshGroupToOpenPosition(meshGroupIndex, asynchPlatingDatas, asynchMeshGroups, asynchMeshGroupTransforms); // and create the trace info so we can select it if (asynchPlatingDatas[meshGroupIndex].meshTraceableData.Count == 0) { PlatingHelper.CreateITraceableForMeshGroup(asynchPlatingDatas, asynchMeshGroups, meshGroupIndex, null); } currentRatioDone += ratioPerMeshGroup; // and put it on the bed PlatingHelper.PlaceMeshGroupOnBed(asynchMeshGroups, asynchMeshGroupTransforms, meshGroupIndex); } // and finally center whatever we have as a group { AxisAlignedBoundingBox bounds = asynchMeshGroups[0].GetAxisAlignedBoundingBox(asynchMeshGroupTransforms[0].TotalTransform); for (int i = 1; i < asynchMeshGroups.Count; i++) { bounds = AxisAlignedBoundingBox.Union(bounds, asynchMeshGroups[i].GetAxisAlignedBoundingBox(asynchMeshGroupTransforms[i].TotalTransform)); } Vector3 boundsCenter = (bounds.maxXYZ + bounds.minXYZ) / 2; for (int i = 0; i < asynchMeshGroups.Count; i++) { ScaleRotateTranslate translate = asynchMeshGroupTransforms[i]; translate.translation *= Matrix4X4.CreateTranslation(-boundsCenter + new Vector3(0, 0, bounds.ZSize / 2)); asynchMeshGroupTransforms[i] = translate; } } }
public void OnMouseUp() { switch (currentTrackingType) { case MouseDownType.Rotation: currentRotationMatrix = currentRotationMatrix * Matrix4X4.CreateRotation(activeRotationQuaternion); activeRotationQuaternion = Quaternion.Identity; OnTransformChanged(null); break; case MouseDownType.Translation: //currentTranslationMatrix = Matrix4X4.Identity; break; case MouseDownType.Scale: break; default: throw new NotImplementedException(); } currentTrackingType = MouseDownType.None; }
public void Reset() { currentRotationMatrix = Matrix4X4.Identity; currentTranslationMatrix = Matrix4X4.Identity; }
public static void MakeLowestFaceFlat(this InteractiveScene scene, IObject3D objectToLayFlatGroup) { var preLayFlatMatrix = objectToLayFlatGroup.Matrix; bool firstVertex = true; IObject3D objectToLayFlat = objectToLayFlatGroup; Vector3Float lowestPosition = Vector3Float.PositiveInfinity; Vector3Float sourceVertexPosition = Vector3Float.NegativeInfinity; IObject3D itemToLayFlat = null; Mesh meshWithLowest = null; var items = objectToLayFlat.VisibleMeshes().Where(i => i.OutputType != PrintOutputTypes.Support); if (!items.Any()) { items = objectToLayFlat.VisibleMeshes(); } // Process each child, checking for the lowest vertex foreach (var itemToCheck in items) { var meshToCheck = itemToCheck.Mesh.GetConvexHull(false); if (meshToCheck == null && meshToCheck.Vertices.Count < 3) { continue; } // find the lowest point on the model for (int testIndex = 0; testIndex < meshToCheck.Vertices.Count; testIndex++) { var vertex = meshToCheck.Vertices[testIndex]; var vertexPosition = vertex.Transform(itemToCheck.WorldMatrix()); if (firstVertex) { meshWithLowest = meshToCheck; lowestPosition = vertexPosition; sourceVertexPosition = vertex; itemToLayFlat = itemToCheck; firstVertex = false; } else if (vertexPosition.Z < lowestPosition.Z) { meshWithLowest = meshToCheck; lowestPosition = vertexPosition; sourceVertexPosition = vertex; itemToLayFlat = itemToCheck; } } } if (meshWithLowest == null) { // didn't find any selected mesh return; } int faceToLayFlat = -1; double largestAreaOfAnyFace = 0; var facesSharingLowestVertex = meshWithLowest.Faces .Select((face, i) => new { face, i }) .Where(faceAndIndex => meshWithLowest.Vertices[faceAndIndex.face.v0] == sourceVertexPosition || meshWithLowest.Vertices[faceAndIndex.face.v1] == sourceVertexPosition || meshWithLowest.Vertices[faceAndIndex.face.v2] == sourceVertexPosition) .Select(j => j.i); var lowestFacesByAngle = facesSharingLowestVertex.OrderBy(i => { var face = meshWithLowest.Faces[i]; var worldNormal = face.normal.TransformNormal(itemToLayFlat.WorldMatrix()); return(worldNormal.CalculateAngle(-Vector3Float.UnitZ)); }); // Check all the faces that are connected to the lowest point to find out which one to lay flat. foreach (var faceIndex in lowestFacesByAngle) { var face = meshWithLowest.Faces[faceIndex]; var worldNormal = face.normal.TransformNormal(itemToLayFlat.WorldMatrix()); var worldAngleDegrees = MathHelper.RadiansToDegrees(worldNormal.CalculateAngle(-Vector3Float.UnitZ)); double largestAreaFound = 0; var faceVeretexIndices = new int[] { face.v0, face.v1, face.v2 }; foreach (var vi in faceVeretexIndices) { if (meshWithLowest.Vertices[vi] != lowestPosition) { var planSurfaceArea = 0.0; foreach (var coPlanarFace in meshWithLowest.GetCoplanerFaces(faceIndex)) { planSurfaceArea += meshWithLowest.GetSurfaceArea(coPlanarFace); } if (largestAreaOfAnyFace == 0 || (planSurfaceArea > largestAreaFound && worldAngleDegrees < 45)) { largestAreaFound = planSurfaceArea; } } } if (largestAreaFound > largestAreaOfAnyFace) { largestAreaOfAnyFace = largestAreaFound; faceToLayFlat = faceIndex; } } double maxDistFromLowestZ = 0; var lowestFace = meshWithLowest.Faces[faceToLayFlat]; var lowestFaceIndices = new int[] { lowestFace.v0, lowestFace.v1, lowestFace.v2 }; var faceVertices = new List <Vector3Float>(); foreach (var vertex in lowestFaceIndices) { var vertexPosition = meshWithLowest.Vertices[vertex].Transform(itemToLayFlat.WorldMatrix()); faceVertices.Add(vertexPosition); maxDistFromLowestZ = Math.Max(maxDistFromLowestZ, vertexPosition.Z - lowestPosition.Z); } if (maxDistFromLowestZ > .001) { var xPositive = (faceVertices[1] - faceVertices[0]).GetNormal(); var yPositive = (faceVertices[2] - faceVertices[0]).GetNormal(); var planeNormal = xPositive.Cross(yPositive).GetNormal(); // this code takes the minimum rotation required and looks much better. Quaternion rotation = new Quaternion(planeNormal, new Vector3Float(0, 0, -1)); Matrix4X4 partLevelMatrix = Matrix4X4.CreateRotation(rotation); // rotate it objectToLayFlat.Matrix = objectToLayFlatGroup.ApplyAtBoundsCenter(partLevelMatrix); } if (objectToLayFlatGroup is Object3D object3D) { AxisAlignedBoundingBox bounds = object3D.GetAxisAlignedBoundingBox(Matrix4X4.Identity, (item) => { return(item.OutputType != PrintOutputTypes.Support); }); Vector3 boundsCenter = (bounds.MaxXYZ + bounds.MinXYZ) / 2; object3D.Matrix *= Matrix4X4.CreateTranslation(new Vector3(0, 0, -boundsCenter.Z + bounds.ZSize / 2)); } else { PlatingHelper.PlaceOnBed(objectToLayFlatGroup); } scene.UndoBuffer.Add(new TransformCommand(objectToLayFlatGroup, preLayFlatMatrix, objectToLayFlatGroup.Matrix)); }
public void Rotate(Quaternion rotation) { currentRotationMatrix = currentRotationMatrix * Matrix4X4.CreateRotation(rotation); OnTransformChanged(null); }
public void Translate(Vector3 deltaPosition) { currentTranslationMatrix = Matrix4X4.CreateTranslation(deltaPosition) * currentTranslationMatrix; OnTransformChanged(null); }
void arrangeMeshGroupsBackgroundWorker_DoWork(object sender, DoWorkEventArgs e) { Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; PushMeshGroupDataToAsynchLists(TraceInfoOpperation.DONT_COPY); BackgroundWorker backgroundWorker = (BackgroundWorker)sender; // move them all out of the way for (int i = 0; i < asynchMeshGroups.Count; i++) { ScaleRotateTranslate translate = asynchMeshGroupTransforms[i]; translate.translation *= Matrix4X4.CreateTranslation(1000, 1000, 0); asynchMeshGroupTransforms[i] = translate; } // sort them by size for (int i = 0; i < asynchMeshGroups.Count; i++) { AxisAlignedBoundingBox iAABB = asynchMeshGroups[i].GetAxisAlignedBoundingBox(asynchMeshGroupTransforms[i].TotalTransform); for (int j = i + 1; j < asynchMeshGroups.Count; j++) { AxisAlignedBoundingBox jAABB = asynchMeshGroups[j].GetAxisAlignedBoundingBox(asynchMeshGroupTransforms[j].TotalTransform); if (Math.Max(iAABB.XSize, iAABB.YSize) < Math.Max(jAABB.XSize, jAABB.YSize)) { PlatingMeshGroupData tempData = asynchPlatingDatas[i]; asynchPlatingDatas[i] = asynchPlatingDatas[j]; asynchPlatingDatas[j] = tempData; MeshGroup tempMeshGroup = asynchMeshGroups[i]; asynchMeshGroups[i] = asynchMeshGroups[j]; asynchMeshGroups[j] = tempMeshGroup; ScaleRotateTranslate iTransform = asynchMeshGroupTransforms[i]; ScaleRotateTranslate jTransform = asynchMeshGroupTransforms[j]; Matrix4X4 tempTransform = iTransform.translation; iTransform.translation = jTransform.translation; jTransform.translation = tempTransform; asynchMeshGroupTransforms[i] = jTransform; asynchMeshGroupTransforms[j] = iTransform; iAABB = jAABB; } } } double ratioPerMeshGroup = 1.0 / asynchMeshGroups.Count; double currentRatioDone = 0; // put them onto the plate (try the center) starting with the biggest and moving down for (int meshGroupIndex = 0; meshGroupIndex < asynchMeshGroups.Count; meshGroupIndex++) { MeshGroup meshGroup = asynchMeshGroups[meshGroupIndex]; Vector3 meshCenter = meshGroup.GetAxisAlignedBoundingBox(asynchMeshGroupTransforms[meshGroupIndex].translation).Center; ScaleRotateTranslate atZero = asynchMeshGroupTransforms[meshGroupIndex]; atZero.translation = Matrix4X4.Identity; asynchMeshGroupTransforms[meshGroupIndex] = atZero; PlatingHelper.MoveMeshGroupToOpenPosition(meshGroupIndex, asynchPlatingDatas, asynchMeshGroups, asynchMeshGroupTransforms); // and create the trace info so we can select it PlatingHelper.CreateITraceableForMeshGroup(asynchPlatingDatas, asynchMeshGroups, meshGroupIndex, (double progress0To1, string processingState, out bool continueProcessing) => { BackgroundWorker_ProgressChanged(progress0To1, processingState, out continueProcessing); }); currentRatioDone += ratioPerMeshGroup; // and put it on the bed PlatingHelper.PlaceMeshGroupOnBed(asynchMeshGroups, asynchMeshGroupTransforms, meshGroupIndex, false); } // and finally center whatever we have as a group { AxisAlignedBoundingBox bounds = asynchMeshGroups[0].GetAxisAlignedBoundingBox(asynchMeshGroupTransforms[0].TotalTransform); for (int i = 1; i < asynchMeshGroups.Count; i++) { bounds = AxisAlignedBoundingBox.Union(bounds, asynchMeshGroups[i].GetAxisAlignedBoundingBox(asynchMeshGroupTransforms[i].TotalTransform)); } Vector3 boundsCenter = (bounds.maxXYZ + bounds.minXYZ) / 2; for (int i = 0; i < asynchMeshGroups.Count; i++) { ScaleRotateTranslate translate = asynchMeshGroupTransforms[i]; translate.translation *= Matrix4X4.CreateTranslation(-boundsCenter + new Vector3(0, 0, bounds.ZSize / 2)); asynchMeshGroupTransforms[i] = translate; } } }
public override void OnMouseDown(MouseEventArgs mouseEvent) { // Show transform override if (activeButtonBeforeMouseOverride == null && mouseEvent.Button == MouseButtons.Right) { activeButtonBeforeMouseOverride = viewControls3D.ActiveButton; viewControls3D.ActiveButton = ViewControls3DButtons.Rotate; } else if (activeButtonBeforeMouseOverride == null && mouseEvent.Button == MouseButtons.Middle) { activeButtonBeforeMouseOverride = viewControls3D.ActiveButton; viewControls3D.ActiveButton = ViewControls3DButtons.Translate; } autoRotating = false; base.OnMouseDown(mouseEvent); if (meshViewerWidget.TrackballTumbleWidget.UnderMouseState == Agg.UI.UnderMouseState.FirstUnderMouse) { if (meshViewerWidget.TrackballTumbleWidget.TransformState == TrackBallController.MouseDownType.None && mouseEvent.Button == MouseButtons.Left && ModifierKeys != Keys.Shift && ModifierKeys != Keys.Control && ModifierKeys != Keys.Alt) { if (!meshViewerWidget.MouseDownOnInteractionVolume) { int meshGroupHitIndex; if (FindMeshGroupHitPosition(mouseEvent.Position, out meshGroupHitIndex)) { meshSelectInfo.hitPlane = new PlaneShape(Vector3.UnitZ, meshSelectInfo.planeDownHitPos.z, null); SelectedMeshGroupIndex = meshGroupHitIndex; transformOnMouseDown = SelectedMeshGroupTransform.translation; Invalidate(); meshSelectInfo.downOnPart = true; } else { SelectedMeshGroupIndex = -1; } UpdateSizeInfo(); } } } }
public async Task AlignObjectHasCorrectPositionsOnXAxis() { StaticData.RootPath = TestContext.CurrentContext.ResolveProjectPath(4, "StaticData"); MatterControlUtilities.OverrideAppDataLocation(TestContext.CurrentContext.ResolveProjectPath(4)); // Automation runner must do as much as program.cs to spin up platform string platformFeaturesProvider = "MatterHackers.MatterControl.WindowsPlatformsFeatures, MatterControl.Winforms"; AppContext.Platform = AggContext.CreateInstanceFrom <INativePlatformFeatures>(platformFeaturesProvider); AppContext.Platform.ProcessCommandline(); var scene = new InteractiveScene(); var cube = await CubeObject3D.Create(20, 20, 20); cube.Matrix = Matrix4X4.CreateTranslation(50, 60, 10); Assert.IsTrue(cube.GetAxisAlignedBoundingBox().Equals(new AxisAlignedBoundingBox(new Vector3(40, 50, 0), new Vector3(60, 70, 20)), .01)); scene.Children.Add(cube); var bigCube = await CubeObject3D.Create(40, 40, 40); bigCube.Matrix = Matrix4X4.CreateTranslation(20, 20, 20); Assert.IsTrue(bigCube.GetAxisAlignedBoundingBox().Equals(new AxisAlignedBoundingBox(new Vector3(0, 0, 0), new Vector3(40, 40, 40)), .01)); scene.Children.Add(bigCube); // select them scene.SelectedItem = cube; scene.AddToSelection(bigCube); // create an align of them var align = new AlignObject3D(); align.AddSelectionAsChildren(scene, scene.SelectedItem); var unalignedBounds = align.GetAxisAlignedBoundingBox(); // assert the align in built correctly Assert.AreEqual(1, scene.Children.Count); Assert.AreEqual(2, align.Children.Count); Assert.IsTrue(align.GetAxisAlignedBoundingBox().Equals(new AxisAlignedBoundingBox(new Vector3(0, 0, 0), new Vector3(60, 70, 40)), 1.0)); align.SelectedChild = new SelectedChildren() { cube.ID.ToString() }; Assert.IsTrue(align.GetAxisAlignedBoundingBox().Equals(unalignedBounds, 1.0)); // turn align on align.XAlign = Align.Min; await align.Rebuild(); Assert.IsTrue(align.GetAxisAlignedBoundingBox().Equals(new AxisAlignedBoundingBox(new Vector3(40, 0, 0), new Vector3(80, 70, 40)), 1.0)); // turn it off align.XAlign = Align.None; await align.Rebuild(); Assert.IsTrue(align.GetAxisAlignedBoundingBox().Equals(unalignedBounds, 1.0)); // turn it back on align.XAlign = Align.Min; await align.Rebuild(); Assert.IsTrue(align.GetAxisAlignedBoundingBox().Equals(new AxisAlignedBoundingBox(new Vector3(40, 0, 0), new Vector3(80, 70, 40)), 1.0)); // remove the align and assert stuff moved back to where it started align.Remove(null); Assert.IsTrue(cube.GetAxisAlignedBoundingBox().Equals(new AxisAlignedBoundingBox(new Vector3(40, 50, 0), new Vector3(60, 70, 20)), .01)); Assert.IsTrue(bigCube.GetAxisAlignedBoundingBox().Equals(new AxisAlignedBoundingBox(new Vector3(0, 0, 0), new Vector3(40, 40, 40)), .01)); }