public void DrawEditor(Object3DControlsLayer layer, DrawEventArgs e) { var diameter = Diameter.Value(this); var startPercent = StartPercent.Value(this); var minSidesPerRotation = MinSidesPerRotation.Value(this); var sourceAabb = this.SourceContainer.GetAxisAlignedBoundingBox(); var distance = diameter / 2 + sourceAabb.YSize / 2; var center = sourceAabb.Center + new Vector3(0, BendDirection == BendDirections.Bend_Up ? distance : -distance, 0); center.X -= sourceAabb.XSize / 2 - (startPercent / 100.0) * sourceAabb.XSize; // render the top and bottom rings layer.World.RenderCylinderOutline(this.WorldMatrix(), center, diameter, sourceAabb.ZSize, 100, Color.Red, Color.Transparent); // render the split lines var radius = diameter / 2; var circumference = MathHelper.Tau * radius; var xxx = sourceAabb.XSize * (startPercent / 100.0); var startAngle = MathHelper.Tau * 3 / 4 - xxx / circumference * MathHelper.Tau; layer.World.RenderCylinderOutline(this.WorldMatrix(), center, diameter, sourceAabb.ZSize, (int)Math.Max(0, Math.Min(100, minSidesPerRotation)), Color.Transparent, Color.Red, phase: startAngle); // turn the lighting back on GL.Enable(EnableCap.Lighting); }
public override Task Rebuild() { this.DebugDepth("Rebuild"); bool valuesChanged = false; // ensure we have good values var startPercent = StartPercent.ClampIfNotCalculated(this, 0, 100, ref valuesChanged); var diameter = Diameter.Value(this); if (diameter == double.MaxValue || diameter == 0) { diameter = DiameterFromAngle(); } // keep the unused type synced so we don't change the bend when clicking the tabs if (BendType == BendTypes.Diameter) { AngleFromDiameter(); } else { diameter = DiameterFromAngle(); } diameter = Diameter.ClampIfNotCalculated(this, .1, 100000, ref valuesChanged); var minSidesPerRotation = MinSidesPerRotation.ClampIfNotCalculated(this, 3, 360, ref valuesChanged); var rebuildLocks = this.RebuilLockAll(); return(ApplicationController.Instance.Tasks.Execute( "Curve".Localize(), null, (reporter, cancellationToken) => { var sourceAabb = this.SourceContainer.GetAxisAlignedBoundingBox(); var radius = diameter / 2; var circumference = MathHelper.Tau * radius; double numRotations = sourceAabb.XSize / circumference; double numberOfCuts = numRotations * minSidesPerRotation; double cutSize = sourceAabb.XSize / numberOfCuts; double cutPosition = sourceAabb.MinXYZ.X + cutSize; var cuts = new List <double>(); for (int i = 0; i < numberOfCuts; i++) { cuts.Add(cutPosition); cutPosition += cutSize; } var rotationCenter = new Vector3(sourceAabb.MinXYZ.X + (sourceAabb.MaxXYZ.X - sourceAabb.MinXYZ.X) * (startPercent / 100), BendDirection == BendDirections.Bend_Up ? sourceAabb.MaxXYZ.Y + radius : sourceAabb.MinXYZ.Y - radius, sourceAabb.Center.Z); var curvedChildren = new List <IObject3D>(); var status = new ProgressStatus(); foreach (var sourceItem in SourceContainer.VisibleMeshes()) { var originalMesh = sourceItem.Mesh; status.Status = "Copy Mesh".Localize(); reporter.Report(status); var transformedMesh = originalMesh.Copy(CancellationToken.None); var itemMatrix = sourceItem.WorldMatrix(SourceContainer); // transform into this space transformedMesh.Transform(itemMatrix); if (SplitMesh) { status.Status = "Split Mesh".Localize(); reporter.Report(status); // split the mesh along the x axis transformedMesh.SplitOnPlanes(Vector3.UnitX, cuts, cutSize / 8); } for (int i = 0; i < transformedMesh.Vertices.Count; i++) { var position = transformedMesh.Vertices[i]; var angleToRotate = ((position.X - rotationCenter.X) / circumference) * MathHelper.Tau - MathHelper.Tau / 4; var distanceFromCenter = rotationCenter.Y - position.Y; if (BendDirection == BendDirections.Bend_Down) { angleToRotate = -angleToRotate; distanceFromCenter = -distanceFromCenter; } var rotatePosition = new Vector3Float(Math.Cos(angleToRotate), Math.Sin(angleToRotate), 0) * distanceFromCenter; rotatePosition.Z = position.Z; transformedMesh.Vertices[i] = rotatePosition + new Vector3Float(rotationCenter.X, radius + sourceAabb.MaxXYZ.Y, 0); } // transform back into item local space transformedMesh.Transform(Matrix4X4.CreateTranslation(-rotationCenter) * itemMatrix.Inverted); if (SplitMesh) { status.Status = "Merge Vertices".Localize(); reporter.Report(status); transformedMesh.MergeVertices(.1); } transformedMesh.CalculateNormals(); var curvedChild = new Object3D() { Mesh = transformedMesh }; curvedChild.CopyWorldProperties(sourceItem, SourceContainer, Object3DPropertyFlags.All, false); curvedChild.Visible = true; curvedChild.Translate(new Vector3(rotationCenter)); if (BendDirection == BendDirections.Bend_Down) { curvedChild.Translate(0, -sourceAabb.YSize - diameter, 0); } curvedChildren.Add(curvedChild); } RemoveAllButSource(); this.SourceContainer.Visible = false; this.Children.Modify((list) => { list.AddRange(curvedChildren); }); UiThread.RunOnIdle(() => { rebuildLocks.Dispose(); Invalidate(InvalidateType.DisplayValues); Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children)); }); return Task.CompletedTask; })); }