public override Task Rebuild() { var rebuildLocks = this.RebuilLockAll(); // spin up a task to calculate the paint return(ApplicationController.Instance.Tasks.Execute("Replacing".Localize(), null, (reporter, cancellationToken) => { try { SubtractAndReplace(cancellationToken, reporter); var newComputedChildren = new SelectedChildren(); foreach (var id in SelectedChildren) { newComputedChildren.Add(id); } ComputedChildren = newComputedChildren; } catch { } rebuildLocks.Dispose(); Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children)); return Task.CompletedTask; })); }
public void AddEditorTransparents(Object3DControlsLayer layer, List <Object3DView> transparentMeshes, DrawEventArgs e) { if (layer.Scene.SelectedItem != null && layer.Scene.SelectedItem == this) { var parentOfSubtractTargets = this.SourceContainer.DescendantsAndSelfMultipleChildrenFirstOrSelf(); var removeObjects = parentOfSubtractTargets.Children .Where(i => SelectedChildren.Contains(i.ID)) .SelectMany(c => c.VisibleMeshes()) .ToList(); foreach (var item in removeObjects) { var color = item.WorldColor(checkOutputType: true); transparentMeshes.Add(new Object3DView(item, color.WithAlpha(color.Alpha0To1 * .2))); } } }
public void DrawEditor(Object3DControlsLayer layer, List <Object3DView> transparentMeshes, DrawEventArgs e) { if (layer.Scene.SelectedItem != null && layer.Scene.SelectedItem == this) { var parentOfSubtractTargets = this.SourceContainer.DescendantsAndSelfMultipleChildrenFirstOrSelf(); var removeObjects = parentOfSubtractTargets.Children .Where(i => SelectedChildren.Contains(i.ID)) .SelectMany(c => c.VisibleMeshes()) .ToList(); foreach (var item in removeObjects) { transparentMeshes.Add(new Object3DView(item, new Color(item.WorldColor(this.SourceContainer), 80))); } var keepItems = parentOfSubtractTargets.Children .Where(i => !SelectedChildren.Contains(i.ID)) .ToList(); foreach (var keepItem in keepItems) { var drawItem = keepItem; var keepItemResult = this.Children.Where(i => i.OwnerID == keepItem.ID).FirstOrDefault(); drawItem = keepItemResult != null ? keepItemResult : drawItem; foreach (var item in drawItem.VisibleMeshes()) { GLHelper.Render(item.Mesh, item.WorldColor(), item.WorldMatrix(), RenderTypes.Outlines, item.WorldMatrix() * layer.World.ModelviewMatrix); } } } }
private static GuiWidget CreateSelector(SelectedChildren childSelector, IObject3D parent, ThemeConfig theme) { GuiWidget tabContainer = new FlowLayoutWidget(FlowDirection.TopToBottom); void UpdateSelectColors(bool selectionChanged = false) { foreach (var child in parent.Children.ToList()) { using (child.RebuildLock()) { if (selectionChanged) { child.Visible = true; } } } } tabContainer.Closed += (s, e) => UpdateSelectColors(); var children = parent.Children.ToList(); var objectChecks = new Dictionary <ICheckbox, IObject3D>(); var radioSiblings = new List <GuiWidget>(); for (int i = 0; i < children.Count; i++) { var itemIndex = i; var child = children[itemIndex]; var rowContainer = new FlowLayoutWidget(); GuiWidget selectWidget; if (children.Count == 2) { var radioButton = new RadioButton(string.IsNullOrWhiteSpace(child.Name) ? $"{itemIndex}" : $"{child.Name}") { Checked = childSelector.Contains(child.ID), TextColor = theme.TextColor }; radioSiblings.Add(radioButton); radioButton.SiblingRadioButtonList = radioSiblings; selectWidget = radioButton; } else { selectWidget = new CheckBox(string.IsNullOrWhiteSpace(child.Name) ? $"{itemIndex}" : $"{child.Name}") { Checked = childSelector.Contains(child.ID), TextColor = theme.TextColor }; } objectChecks.Add((ICheckbox)selectWidget, child); rowContainer.AddChild(selectWidget); var checkBox = selectWidget as ICheckbox; checkBox.CheckedStateChanged += (s, e) => { if (s is ICheckbox checkbox) { if (checkBox.Checked) { if (!childSelector.Contains(objectChecks[checkbox].ID)) { childSelector.Add(objectChecks[checkbox].ID); } } else { if (childSelector.Contains(objectChecks[checkbox].ID)) { childSelector.Remove(objectChecks[checkbox].ID); } } if (parent is MeshWrapperObject3D meshWrapper) { using (meshWrapper.RebuildLock()) { meshWrapper.ResetMeshWrapperMeshes(Object3DPropertyFlags.All, CancellationToken.None); } } UpdateSelectColors(true); } }; tabContainer.AddChild(rowContainer); UpdateSelectColors(); } return(tabContainer); }
private static GuiWidget CreateSourceChildSelector(SelectedChildren childSelector, OperationSourceContainerObject3D sourceContainer, ThemeConfig theme, Action selectionChanged) { GuiWidget tabContainer = new FlowLayoutWidget(FlowDirection.TopToBottom) { Margin = new BorderDouble(0, 3, 0, 0), }; var parentOfSubtractTargets = sourceContainer.SourceContainer.DescendantsAndSelfMultipleChildrenFirstOrSelf(); var sourceChildren = parentOfSubtractTargets.Children.ToList(); var objectChecks = new Dictionary <ICheckbox, IObject3D>(); var radioSiblings = new List <GuiWidget>(); for (int i = 0; i < sourceChildren.Count; i++) { var itemIndex = i; var child = sourceChildren[itemIndex]; var rowContainer = new FlowLayoutWidget() { Padding = new BorderDouble(15, 0, 0, 3) }; GuiWidget selectWidget; if (sourceChildren.Count == 2) { var radioButton = new RadioButton(string.IsNullOrWhiteSpace(child.Name) ? $"{itemIndex}" : $"{child.Name}") { Checked = childSelector.Contains(child.ID), TextColor = theme.TextColor, Margin = 0, }; radioSiblings.Add(radioButton); radioButton.SiblingRadioButtonList = radioSiblings; selectWidget = radioButton; } else { selectWidget = new CheckBox(string.IsNullOrWhiteSpace(child.Name) ? $"{itemIndex}" : $"{child.Name}") { Checked = childSelector.Contains(child.ID), TextColor = theme.TextColor, }; } objectChecks.Add((ICheckbox)selectWidget, child); rowContainer.AddChild(selectWidget); var checkBox = selectWidget as ICheckbox; checkBox.CheckedStateChanged += (s, e) => { if (s is ICheckbox checkbox) { if (checkBox.Checked) { if (!childSelector.Contains(objectChecks[checkbox].ID)) { childSelector.Add(objectChecks[checkbox].ID); } } else { if (childSelector.Contains(objectChecks[checkbox].ID)) { childSelector.Remove(objectChecks[checkbox].ID); } } selectionChanged?.Invoke(); } }; tabContainer.AddChild(rowContainer); } return(tabContainer); }
public static GuiWidget CreatePropertyEditor(EditableProperty property, UndoBuffer undoBuffer, PPEContext context, ThemeConfig theme) { var object3D = property.Item; var propertyGridModifier = property.Item as IPropertyGridModifier; GuiWidget rowContainer = null; // Get reflected property value once, then test for each case below var propertyValue = property.Value; void RegisterValueChanged(UIField field, Func <string, object> valueFromString, Func <object, string> valueToString = null) { field.ValueChanged += (s, e) => { var newValue = field.Value; var oldValue = property.Value.ToString(); if (valueToString != null) { oldValue = valueToString(property.Value); } // field.Content if (undoBuffer != null) { undoBuffer.AddAndDo(new UndoRedoActions(() => { property.SetValue(valueFromString(oldValue)); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); }, () => { property.SetValue(valueFromString(newValue)); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); })); } else { property.SetValue(valueFromString(newValue)); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); } }; } var readOnly = property.PropertyInfo.GetCustomAttributes(true).OfType <ReadOnlyAttribute>().FirstOrDefault() != null; // create a double editor if (propertyValue is double doubleValue) { if (readOnly) { var valueField = new TextWidget(string.Format("{0:n}", doubleValue), textColor: theme.TextColor, pointSize: 10) { AutoExpandBoundsToText = true }; rowContainer = new SettingsRow(property.DisplayName.Localize(), property.Description, valueField, theme); void RefreshField(object s, InvalidateArgs e) { if (e.InvalidateType.HasFlag(InvalidateType.DisplayValues)) { double newValue = (double)property.Value; valueField.Text = string.Format("{0:n}", newValue); } } object3D.Invalidated += RefreshField; valueField.Closed += (s, e) => object3D.Invalidated -= RefreshField; } else // normal edit row { var field = new DoubleField(theme); field.Initialize(0); field.DoubleValue = doubleValue; field.ClearUndoHistory(); RegisterValueChanged(field, (valueString) => { return(double.Parse(valueString)); }); void RefreshField(object s, InvalidateArgs e) { if (e.InvalidateType.HasFlag(InvalidateType.DisplayValues)) { double newValue = (double)property.Value; if (newValue != field.DoubleValue) { field.DoubleValue = newValue; } } } object3D.Invalidated += RefreshField; field.Content.Descendants <InternalTextEditWidget>().First().Name = property.DisplayName + " Edit"; field.Content.Closed += (s, e) => object3D.Invalidated -= RefreshField; rowContainer = CreateSettingsRow(property, field, theme); } } else if (propertyValue is Color color) { var field = new ColorField(theme, object3D.Color); field.Initialize(0); field.ValueChanged += (s, e) => { property.SetValue(field.Color); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); }; rowContainer = CreateSettingsRow(property, field, theme); } else if (propertyValue is Vector2 vector2) { var field = new Vector2Field(theme); field.Initialize(0); field.Vector2 = vector2; field.ClearUndoHistory(); RegisterValueChanged(field, (valueString) => Vector2.Parse(valueString), (value) => { var s = ((Vector2)value).ToString(); return(s.Substring(1, s.Length - 2)); }); rowContainer = CreateSettingsColumn(property, field); } else if (propertyValue is Vector3 vector3) { var field = new Vector3Field(theme); field.Initialize(0); field.Vector3 = vector3; field.ClearUndoHistory(); RegisterValueChanged( field, (valueString) => Vector3.Parse(valueString), (value) => { var s = ((Vector3)value).ToString(); return(s.Substring(1, s.Length - 2)); }); rowContainer = CreateSettingsColumn(property, field); } else if (propertyValue is DirectionVector directionVector) { var field = new DirectionVectorField(theme); field.Initialize(0); field.SetValue(directionVector); field.ClearUndoHistory(); field.ValueChanged += (s, e) => { property.SetValue(field.DirectionVector); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); }; rowContainer = CreateSettingsRow(property, field, theme); } else if (propertyValue is DirectionAxis directionAxis) { rowContainer = CreateSettingsColumn(property); var field1 = new DirectionVectorField(theme); field1.Initialize(0); field1.ClearUndoHistory(); field1.SetValue(new DirectionVector() { Normal = directionAxis.Normal }); rowContainer.AddChild(new SettingsRow("Axis".Localize(), null, field1.Content, theme)); // the direction axis // the distance from the center of the part // create a double editor var field2 = new Vector3Field(theme); field2.Initialize(0); field2.Vector3 = directionAxis.Origin - property.Item.Children.First().GetAxisAlignedBoundingBox().Center; field2.ClearUndoHistory(); var row2 = CreateSettingsColumn("Offset".Localize(), field2); // update this when changed void UpdateData(object s, InvalidateArgs e) { field2.Vector3 = ((DirectionAxis)property.Value).Origin - property.Item.Children.First().GetAxisAlignedBoundingBox().Center; } property.Item.Invalidated += UpdateData; field2.Content.Closed += (s, e) => { property.Item.Invalidated -= UpdateData; }; // update functions field1.ValueChanged += (s, e) => { property.SetValue(new DirectionAxis() { Normal = field1.DirectionVector.Normal, Origin = property.Item.Children.First().GetAxisAlignedBoundingBox().Center + field2.Vector3 }); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); }; field2.ValueChanged += (s, e) => { property.SetValue(new DirectionAxis() { Normal = field1.DirectionVector.Normal, Origin = property.Item.Children.First().GetAxisAlignedBoundingBox().Center + field2.Vector3 }); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); }; rowContainer.AddChild(row2); } else if (propertyValue is SelectedChildren childSelector) { var showAsList = property.PropertyInfo.GetCustomAttributes(true).OfType <ShowAsListAttribute>().FirstOrDefault() != null; if (showAsList) { UIField field = new ChildrenSelectorListField(property, theme); field.Initialize(0); RegisterValueChanged(field, (valueString) => { var childrenSelector = new SelectedChildren(); foreach (var child in valueString.Split(',')) { childrenSelector.Add(child); } return(childrenSelector); }); rowContainer = CreateSettingsRow(property, field, theme); } else // show the subtract editor for boolean subtract and subtract and replace { rowContainer = CreateSettingsColumn(property); if (property.Item is OperationSourceContainerObject3D sourceContainer) { Action selected = null; if (!(context.item.GetType().GetCustomAttributes(typeof(ShowUpdateButtonAttribute), true).FirstOrDefault() is ShowUpdateButtonAttribute showUpdate)) { selected = () => { object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties)); }; } rowContainer.AddChild(CreateSourceChildSelector(childSelector, sourceContainer, theme, selected)); } else { rowContainer.AddChild(CreateSelector(childSelector, property.Item, theme)); } } } else if (propertyValue is ImageBuffer imageBuffer) { rowContainer = CreateSettingsColumn(property); rowContainer.AddChild(new ImageWidget(imageBuffer) { HAnchor = HAnchor.Left, Margin = new BorderDouble(0, 3) }); } #if !__ANDROID__ else if (propertyValue is List <string> stringList) { var field = new SurfacedEditorsField(theme, property.Item); field.Initialize(0); field.ListValue = stringList; field.ValueChanged += (s, e) => { property.SetValue(field.ListValue); }; rowContainer = CreateSettingsColumn(property, field); rowContainer.Descendants <HorizontalSpacer>().FirstOrDefault()?.Close(); } #endif // create a int editor else if (propertyValue is int intValue) { if (readOnly) { var valueField = new TextWidget(string.Format("{0:n0}", intValue), textColor: theme.TextColor, pointSize: 10) { AutoExpandBoundsToText = true }; rowContainer = new SettingsRow(property.DisplayName.Localize(), property.Description, valueField, theme); void RefreshField(object s, InvalidateArgs e) { if (e.InvalidateType.HasFlag(InvalidateType.DisplayValues)) { int newValue = (int)property.Value; valueField.Text = string.Format("{0:n0}", newValue); } } object3D.Invalidated += RefreshField; valueField.Closed += (s, e) => object3D.Invalidated -= RefreshField; } else // normal edit row { var field = new IntField(theme); field.Initialize(0); field.IntValue = intValue; field.ClearUndoHistory(); RegisterValueChanged(field, (valueString) => { return(int.Parse(valueString)); }); void RefreshField(object s, InvalidateArgs e) { if (e.InvalidateType.HasFlag(InvalidateType.DisplayValues)) { int newValue = (int)property.Value; if (newValue != field.IntValue) { field.IntValue = newValue; } } } object3D.Invalidated += RefreshField; field.Content.Closed += (s, e) => object3D.Invalidated -= RefreshField; rowContainer = CreateSettingsRow(property, field, theme); } } else if (propertyValue is bool boolValue) { // create a bool editor var field = new ToggleboxField(theme); field.Initialize(0); field.Checked = boolValue; RegisterValueChanged(field, (valueString) => { return(valueString == "1"); }, (value) => { return(((bool)value) ? "1" : "0"); }); rowContainer = CreateSettingsRow(property, field, theme); } else if (propertyValue is string stringValue) { if (readOnly) { rowContainer = new WrappedTextWidget(stringValue, textColor: theme.TextColor, pointSize: 10) { Margin = 5 }; } else // normal edit row { var multiLineEditAttribute = property.PropertyInfo.GetCustomAttributes(true).OfType <MultiLineEditAttribute>().FirstOrDefault(); if (multiLineEditAttribute != null) { // create a a multi-line string editor var field = new MultilineStringField(theme); field.Initialize(0); field.SetValue(stringValue, false); field.ClearUndoHistory(); field.Content.HAnchor = HAnchor.Stretch; // field.Content.MinimumSize = new Vector2(0, 200 * GuiWidget.DeviceScale); RegisterValueChanged(field, (valueString) => valueString); rowContainer = CreateSettingsColumn(property, field, fullWidth: true); } else { // create a string editor var field = new TextField(theme); field.Initialize(0); field.SetValue(stringValue, false); field.ClearUndoHistory(); field.Content.HAnchor = HAnchor.Stretch; RegisterValueChanged(field, (valueString) => valueString); rowContainer = CreateSettingsRow(property, field, theme); var label = rowContainer.Children.First(); var spacer = rowContainer.Children.OfType <HorizontalSpacer>().FirstOrDefault(); spacer.HAnchor = HAnchor.Absolute; spacer.Width = Math.Max(0, 100 - label.Width); } } } else if (propertyValue is char charValue) { // create a char editor var field = new CharField(theme); field.Initialize(0); field.SetValue(charValue.ToString(), false); field.ClearUndoHistory(); field.ValueChanged += (s, e) => { property.SetValue(Convert.ToChar(field.Value)); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); }; rowContainer = CreateSettingsRow(property, field, theme); } else if (property.PropertyType.IsEnum) { // create an enum editor UIField field; var enumDisplayAttribute = property.PropertyInfo.GetCustomAttributes(true).OfType <EnumDisplayAttribute>().FirstOrDefault(); var addToSettingsRow = true; if (enumDisplayAttribute != null) { field = new EnumDisplayField(property, enumDisplayAttribute, theme) { InitialValue = propertyValue.ToString(), }; if (enumDisplayAttribute.Mode == EnumDisplayAttribute.PresentationMode.Tabs) { addToSettingsRow = false; } } else { if (property.PropertyType == typeof(NamedTypeFace)) { field = new FontSelectorField(property, theme); } else { field = new EnumField(property, theme); } } field.Initialize(0); RegisterValueChanged(field, (valueString) => { return(Enum.Parse(property.PropertyType, valueString)); }); field.ValueChanged += (s, e) => { if (property.Value.ToString() != field.Value) { property.SetValue(Enum.Parse(property.PropertyType, field.Value)); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); } }; if (addToSettingsRow) { rowContainer = CreateSettingsRow(property, field, theme); } else { // field.Content.Margin = new BorderDouble(3, 0); field.Content.HAnchor = HAnchor.Stretch; rowContainer = field.Content; } } else if (propertyValue is IObject3D item && ApplicationController.Instance.Extensions.GetEditorsForType(property.PropertyType)?.FirstOrDefault() is IObject3DEditor iObject3DEditor) { // Use known IObject3D editors rowContainer = iObject3DEditor.Create(item, undoBuffer, theme); } // remember the row name and widget context.editRows.Add(property.PropertyInfo.Name, rowContainer); return(rowContainer); }
private void SubtractAndReplace(CancellationToken cancellationToken, IProgress <ProgressStatus> reporter) { SourceContainer.Visible = true; RemoveAllButSource(); var parentOfPaintTargets = SourceContainer.DescendantsAndSelfMultipleChildrenFirstOrSelf(); if (parentOfPaintTargets.Children.Count() < 2) { if (parentOfPaintTargets.Children.Count() == 1) { this.Children.Add(SourceContainer.Clone()); SourceContainer.Visible = false; } return; } SubtractObject3D_2.CleanUpSelectedChildrenNames(this); var paintObjects = parentOfPaintTargets.Children .Where((i) => SelectedChildren .Contains(i.ID)) .SelectMany(c => c.VisibleMeshes()) .ToList(); var keepItems = parentOfPaintTargets.Children .Where((i) => !SelectedChildren .Contains(i.ID)); var keepVisibleItems = keepItems.SelectMany(c => c.VisibleMeshes()).ToList(); if (paintObjects.Any() && keepVisibleItems.Any()) { var totalOperations = paintObjects.Count * keepVisibleItems.Count; double amountPerOperation = 1.0 / totalOperations; double ratioCompleted = 0; var progressStatus = new ProgressStatus { Status = "Do CSG" }; foreach (var keep in keepVisibleItems) { var keepResultsMesh = keep.Mesh; var keepWorldMatrix = keep.WorldMatrix(SourceContainer); foreach (var paint in paintObjects) { if (cancellationToken.IsCancellationRequested) { SourceContainer.Visible = true; RemoveAllButSource(); return; } Mesh paintMesh = BooleanProcessing.Do(keepResultsMesh, keepWorldMatrix, // paint data paint.Mesh, paint.WorldMatrix(SourceContainer), // operation type CsgModes.Intersect, Processing, InputResolution, OutputResolution, // reporting data reporter, amountPerOperation, ratioCompleted, progressStatus, cancellationToken); keepResultsMesh = BooleanProcessing.Do(keepResultsMesh, keepWorldMatrix, // point data paint.Mesh, paint.WorldMatrix(SourceContainer), // operation type CsgModes.Subtract, Processing, InputResolution, OutputResolution, // reporting data reporter, amountPerOperation, ratioCompleted, progressStatus, cancellationToken); // after the first time we get a result the results mesh is in the right coordinate space keepWorldMatrix = Matrix4X4.Identity; // store our intersection (paint) results mesh var paintResultsItem = new Object3D() { Mesh = paintMesh, Visible = false, OwnerID = paint.ID }; // copy all the properties but the matrix paintResultsItem.CopyWorldProperties(paint, SourceContainer, Object3DPropertyFlags.All & (~(Object3DPropertyFlags.Matrix | Object3DPropertyFlags.Visible))); // and add it to this this.Children.Add(paintResultsItem); // report our progress ratioCompleted += amountPerOperation; progressStatus.Progress0To1 = ratioCompleted; reporter?.Report(progressStatus); } // store our results mesh var keepResultsItem = new Object3D() { Mesh = keepResultsMesh, Visible = false, OwnerID = keep.ID }; // copy all the properties but the matrix keepResultsItem.CopyWorldProperties(keep, SourceContainer, Object3DPropertyFlags.All & (~(Object3DPropertyFlags.Matrix | Object3DPropertyFlags.Visible))); // and add it to this this.Children.Add(keepResultsItem); } foreach (var child in Children) { child.Visible = true; } SourceContainer.Visible = false; } }
private void Subtract(CancellationToken cancellationToken, IProgress <ProgressStatus> reporter) { SourceContainer.Visible = true; RemoveAllButSource(); var parentOfSubtractTargets = SourceContainer.DescendantsAndSelfMultipleChildrenFirstOrSelf(); if (parentOfSubtractTargets.Children.Count() < 2) { if (parentOfSubtractTargets.Children.Count() == 1) { this.Children.Add(SourceContainer.Clone()); SourceContainer.Visible = false; } return; } CleanUpSelectedChildrenNames(this); var removeVisibleItems = parentOfSubtractTargets.Children .Where((i) => SelectedChildren .Contains(i.ID)) .SelectMany(c => c.VisiblePaths()) .ToList(); var keepItems = parentOfSubtractTargets.Children .Where((i) => !SelectedChildren .Contains(i.ID)); var keepVisibleItems = keepItems.SelectMany(c => c.VisiblePaths()).ToList(); if (removeVisibleItems.Any() && keepVisibleItems.Any()) { var totalOperations = removeVisibleItems.Count * keepVisibleItems.Count; double amountPerOperation = 1.0 / totalOperations; double percentCompleted = 0; var progressStatus = new ProgressStatus { Status = "Do Subtract" }; bool first = true; foreach (var keep in keepVisibleItems) { var resultsVertexSource = (keep as IPathObject).VertexSource.Transform(keep.Matrix); foreach (var remove in removeVisibleItems) { resultsVertexSource = resultsVertexSource.MergePaths(((IPathObject)remove).VertexSource.Transform(remove.Matrix), ClipperLib.ClipType.ctDifference); // report our progress percentCompleted += amountPerOperation; progressStatus.Progress0To1 = percentCompleted; reporter?.Report(progressStatus); } if (first) { this.VertexSource = resultsVertexSource; first = false; } else { this.VertexSource.MergePaths(resultsVertexSource, ClipperLib.ClipType.ctUnion); } } // this.VertexSource = this.VertexSource.Transform(Matrix.Inverted); first = true; foreach (var child in Children) { if (first) { // hide the source item child.Visible = false; first = false; } else { child.Visible = true; } } } }
private void Subtract(CancellationToken cancellationToken, IProgress <ProgressStatus> reporter) { SourceContainer.Visible = true; RemoveAllButSource(); var parentOfSubtractTargets = SourceContainer.DescendantsAndSelfMultipleChildrenFirstOrSelf(); if (parentOfSubtractTargets.Children.Count() < 2) { if (parentOfSubtractTargets.Children.Count() == 1) { this.Children.Add(SourceContainer.Clone()); SourceContainer.Visible = false; } return; } CleanUpSelectedChildrenNames(this); var removeVisibleItems = parentOfSubtractTargets.Children .Where((i) => SelectedChildren .Contains(i.ID)) .SelectMany(c => c.VisibleMeshes()) .ToList(); var keepItems = parentOfSubtractTargets.Children .Where((i) => !SelectedChildren .Contains(i.ID)); var keepVisibleItems = keepItems.SelectMany(c => c.VisibleMeshes()).ToList(); if (removeVisibleItems.Any() && keepVisibleItems.Any()) { var totalOperations = removeVisibleItems.Count * keepVisibleItems.Count; double amountPerOperation = 1.0 / totalOperations; double percentCompleted = 0; var progressStatus = new ProgressStatus { Status = "Do CSG" }; foreach (var keep in keepVisibleItems) { var resultsMesh = keep.Mesh; var keepWorldMatrix = keep.WorldMatrix(SourceContainer); foreach (var remove in removeVisibleItems) { resultsMesh = BooleanProcessing.Do(resultsMesh, keepWorldMatrix, // other mesh remove.Mesh, remove.WorldMatrix(SourceContainer), // operation type BooleanProcessing.CsgModes.Subtract, Processing, InputResolution, OutputResolution, // reporting reporter, amountPerOperation, percentCompleted, progressStatus, cancellationToken); // after the first time we get a result the results mesh is in the right coordinate space keepWorldMatrix = Matrix4X4.Identity; // report our progress percentCompleted += amountPerOperation; progressStatus.Progress0To1 = percentCompleted; reporter?.Report(progressStatus); } // store our results mesh var resultsItem = new Object3D() { Mesh = resultsMesh, Visible = false, OwnerID = keep.ID }; // copy all the properties but the matrix resultsItem.CopyWorldProperties(keep, SourceContainer, Object3DPropertyFlags.All & (~(Object3DPropertyFlags.Matrix | Object3DPropertyFlags.Visible))); // and add it to this this.Children.Add(resultsItem); } bool first = true; foreach (var child in Children) { if (first) { // hid the source item child.Visible = false; first = false; } else { child.Visible = true; } } } }
public static GuiWidget CreatePropertyEditor(EditableProperty property, UndoBuffer undoBuffer, PPEContext context, ThemeConfig theme) { var object3D = property.Item; var propertyGridModifier = property.Item as IPropertyGridModifier; GuiWidget rowContainer = null; // Get reflected property value once, then test for each case below var propertyValue = property.Value; void RegisterValueChanged(UIField field, Func <string, object> valueFromString, Func <object, string> valueToString = null) { field.ValueChanged += (s, e) => { var newValue = field.Value; var oldValue = property.Value.ToString(); if (valueToString != null) { oldValue = valueToString(property.Value); } //field.Content if (undoBuffer != null) { undoBuffer.AddAndDo(new UndoRedoActions(() => { property.SetValue(valueFromString(oldValue)); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); }, () => { property.SetValue(valueFromString(newValue)); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); })); } else { property.SetValue(valueFromString(newValue)); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); } }; } // create a double editor if (propertyValue is double doubleValue) { var field = new DoubleField(theme); field.Initialize(0); field.DoubleValue = doubleValue; RegisterValueChanged(field, (valueString) => { return(double.Parse(valueString)); }); void RefreshField(object s, InvalidateArgs e) { if (e.InvalidateType.HasFlag(InvalidateType.DisplayValues)) { double newValue = (double)property.Value; if (newValue != field.DoubleValue) { field.DoubleValue = newValue; } } } object3D.Invalidated += RefreshField; field.Content.Closed += (s, e) => object3D.Invalidated -= RefreshField; rowContainer = CreateSettingsRow(property, field); } else if (propertyValue is Color color) { var field = new ColorField(theme, object3D.Color); field.Initialize(0); field.ValueChanged += (s, e) => { property.SetValue(field.Color); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); }; rowContainer = CreateSettingsRow(property, field); } else if (propertyValue is Vector2 vector2) { var field = new Vector2Field(theme); field.Initialize(0); field.Vector2 = vector2; RegisterValueChanged(field, (valueString) => Vector2.Parse(valueString), (value) => { var s = ((Vector2)value).ToString(); return(s.Substring(1, s.Length - 2)); }); rowContainer = CreateSettingsColumn(property, field); } else if (propertyValue is Vector3 vector3) { var field = new Vector3Field(theme); field.Initialize(0); field.Vector3 = vector3; RegisterValueChanged( field, (valueString) => Vector3.Parse(valueString), (value) => { var s = ((Vector3)value).ToString(); return(s.Substring(1, s.Length - 2)); }); rowContainer = CreateSettingsColumn(property, field); } else if (propertyValue is DirectionVector directionVector) { var field = new DirectionVectorField(theme); field.Initialize(0); field.SetValue(directionVector); field.ValueChanged += (s, e) => { property.SetValue(field.DirectionVector); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); }; rowContainer = CreateSettingsRow(property, field); } else if (propertyValue is DirectionAxis directionAxis) { rowContainer = CreateSettingsColumn(property); var newDirectionVector = new DirectionVector() { Normal = directionAxis.Normal }; var row1 = CreateSettingsRow("Axis".Localize()); var field1 = new DirectionVectorField(theme); field1.Initialize(0); field1.SetValue(newDirectionVector); row1.AddChild(field1.Content); rowContainer.AddChild(row1); // the direction axis // the distance from the center of the part // create a double editor var field2 = new Vector3Field(theme); field2.Initialize(0); field2.Vector3 = directionAxis.Origin - property.Item.Children.First().GetAxisAlignedBoundingBox().Center; var row2 = CreateSettingsColumn("Offset", field2); // update this when changed EventHandler <InvalidateArgs> updateData = (s, e) => { field2.Vector3 = ((DirectionAxis)property.Value).Origin - property.Item.Children.First().GetAxisAlignedBoundingBox().Center; }; property.Item.Invalidated += updateData; field2.Content.Closed += (s, e) => { property.Item.Invalidated -= updateData; }; // update functions field1.ValueChanged += (s, e) => { property.SetValue(new DirectionAxis() { Normal = field1.DirectionVector.Normal, Origin = property.Item.Children.First().GetAxisAlignedBoundingBox().Center + field2.Vector3 }); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); }; field2.ValueChanged += (s, e) => { property.SetValue(new DirectionAxis() { Normal = field1.DirectionVector.Normal, Origin = property.Item.Children.First().GetAxisAlignedBoundingBox().Center + field2.Vector3 }); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); }; rowContainer.AddChild(row2); } else if (propertyValue is SelectedChildren childSelector) { var showAsList = property.PropertyInfo.GetCustomAttributes(true).OfType <ShowAsListAttribute>().FirstOrDefault() != null; if (showAsList) { UIField field = new ChildrenSelectorListField(property, theme); field.Initialize(0); RegisterValueChanged(field, (valueString) => { var childrenSelector = new SelectedChildren(); foreach (var child in valueString.Split(',')) { childrenSelector.Add(child); } return(childrenSelector); }); rowContainer = CreateSettingsRow(property, field); } else // show the subtract editor for boolean subtract and subtract and replace { rowContainer = CreateSettingsColumn(property); if (property.Item is OperationSourceContainerObject3D sourceContainer) { rowContainer.AddChild(CreateSourceChildSelector(childSelector, sourceContainer, theme)); } else { rowContainer.AddChild(CreateSelector(childSelector, property.Item, theme)); } } } else if (propertyValue is ImageBuffer imageBuffer) { rowContainer = CreateSettingsColumn(property); rowContainer.AddChild(CreateImageDisplay(imageBuffer, property.Item, theme)); } #if !__ANDROID__ else if (propertyValue is List <string> stringList) { var selectedItem = ApplicationController.Instance.DragDropData.SceneContext.Scene.SelectedItem; var field = new SurfacedEditorsField(theme, selectedItem); field.Initialize(0); field.ListValue = stringList; field.ValueChanged += (s, e) => { property.SetValue(field.ListValue); }; rowContainer = CreateSettingsColumn(property, field); rowContainer.Descendants <HorizontalSpacer>().FirstOrDefault()?.Close(); } #endif // create a int editor else if (propertyValue is int intValue) { var field = new IntField(theme); field.Initialize(0); field.IntValue = intValue; RegisterValueChanged(field, (valueString) => { return(int.Parse(valueString)); }); void RefreshField(object s, InvalidateArgs e) { if (e.InvalidateType.HasFlag(InvalidateType.DisplayValues)) { int newValue = (int)property.Value; if (newValue != field.IntValue) { field.IntValue = newValue; } } } object3D.Invalidated += RefreshField; field.Content.Closed += (s, e) => object3D.Invalidated -= RefreshField; rowContainer = CreateSettingsRow(property, field); } // create a bool editor else if (propertyValue is bool boolValue) { var field = new ToggleboxField(theme); field.Initialize(0); field.Checked = boolValue; RegisterValueChanged(field, (valueString) => { return(valueString == "1"); }, (value) => { return(((bool)(value)) ? "1" : "0"); }); rowContainer = CreateSettingsRow(property, field); } // create a string editor else if (propertyValue is string stringValue) { var field = new TextField(theme); field.Initialize(0); field.SetValue(stringValue, false); field.Content.HAnchor = HAnchor.Stretch; RegisterValueChanged(field, (valueString) => valueString); rowContainer = CreateSettingsRow(property, field); var label = rowContainer.Children.First(); if (field is TextField) { var spacer = rowContainer.Children.OfType <HorizontalSpacer>().FirstOrDefault(); spacer.HAnchor = HAnchor.Absolute; spacer.Width = Math.Max(0, 100 - label.Width); } } // create a char editor else if (propertyValue is char charValue) { var field = new CharField(theme); field.Initialize(0); field.SetValue(charValue.ToString(), false); field.ValueChanged += (s, e) => { property.SetValue(Convert.ToChar(field.Value)); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); }; rowContainer = CreateSettingsRow(property, field); } // create an enum editor else if (property.PropertyType.IsEnum) { UIField field; var iconsAttribute = property.PropertyInfo.GetCustomAttributes(true).OfType <IconsAttribute>().FirstOrDefault(); if (iconsAttribute != null) { field = new IconEnumField(property, iconsAttribute, theme) { InitialValue = propertyValue.ToString() }; } else { field = new EnumField(property, theme); } field.Initialize(0); RegisterValueChanged(field, (valueString) => { return(Enum.Parse(property.PropertyType, valueString)); }); field.ValueChanged += (s, e) => { property.SetValue(Enum.Parse(property.PropertyType, field.Value)); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); }; rowContainer = CreateSettingsRow(property, field, theme); } // Use known IObject3D editors else if (propertyValue is IObject3D item && ApplicationController.Instance.Extensions.GetEditorsForType(property.PropertyType)?.FirstOrDefault() is IObject3DEditor iObject3DEditor) { rowContainer = iObject3DEditor.Create(item, undoBuffer, theme); } // remember the row name and widget context.editRows.Add(property.PropertyInfo.Name, rowContainer); return(rowContainer); }
public void DrawEditor(Object3DControlsLayer layer, List <Object3DView> transparentMeshes, DrawEventArgs e) { var parentOfSourceItems = this.SourceContainer.DescendantsAndSelfMultipleChildrenFirstOrSelf(); var sourceItems = parentOfSourceItems.Children.ToList(); foreach (var paintItem in sourceItems) { var paintItemResults = this.Children.Where(i => i.OwnerID == paintItem.ID); var wasSelected = ComputedChildren.Contains(paintItem.ID); var currentlySelected = SelectedChildren.Contains(paintItem.ID); if (currentlySelected) { // if this is selected always paint a transparent source foreach (var item in paintItem.VisibleMeshes()) { transparentMeshes.Add(new Object3DView(item, new Color(item.WorldColor(this.SourceContainer), 80))); } // if it was also selected in before (the results are right) if (wasSelected) { // paint solid results if (paintItemResults != null) { foreach (var paintItemResult in paintItemResults) { foreach (var item in paintItemResult.VisibleMeshes()) { GLHelper.Render(item.Mesh, item.WorldColor(), item.WorldMatrix(), RenderTypes.Outlines, item.WorldMatrix() * layer.World.ModelviewMatrix); } } } } } else if (wasSelected) { // it is not selected now but was selected before (changed state) // pant the solid source foreach (var item in paintItem.VisibleMeshes()) { GLHelper.Render(item.Mesh, item.WorldColor(), item.WorldMatrix(), RenderTypes.Outlines, item.WorldMatrix() * layer.World.ModelviewMatrix); } } else // it is not selected now and was not before (same state) { // paint the results if (paintItemResults != null && paintItemResults.Count() > 0) { foreach (var paintItemResult in paintItemResults) { foreach (var item in paintItemResult.VisibleMeshes()) { GLHelper.Render(item.Mesh, item.WorldColor(), item.WorldMatrix(), RenderTypes.Outlines, item.WorldMatrix() * layer.World.ModelviewMatrix); } } } else // we don't have any results yet { foreach (var item in paintItem.VisibleMeshes()) { GLHelper.Render(item.Mesh, item.WorldColor(), item.WorldMatrix(), RenderTypes.Outlines, item.WorldMatrix() * layer.World.ModelviewMatrix); } } } } }
private static GuiWidget CreateSourceChildSelector(SelectedChildren childSelector, OperationSourceContainerObject3D sourceCantainer, ThemeConfig theme) { GuiWidget tabContainer = new FlowLayoutWidget(FlowDirection.TopToBottom); var sourceChildren = sourceCantainer.SourceContainer.VisibleMeshes().ToList(); var objectChecks = new Dictionary <ICheckbox, IObject3D>(); var radioSiblings = new List <GuiWidget>(); for (int i = 0; i < sourceChildren.Count; i++) { var itemIndex = i; var child = sourceChildren[itemIndex]; var rowContainer = new FlowLayoutWidget(); GuiWidget selectWidget; if (sourceChildren.Count == 2) { var radioButton = new RadioButton(string.IsNullOrWhiteSpace(child.Name) ? $"{itemIndex}" : $"{child.Name}") { Checked = childSelector.Contains(child.Name), TextColor = theme.TextColor }; radioSiblings.Add(radioButton); radioButton.SiblingRadioButtonList = radioSiblings; selectWidget = radioButton; } else { selectWidget = new CheckBox(string.IsNullOrWhiteSpace(child.Name) ? $"{itemIndex}" : $"{child.Name}") { Checked = childSelector.Contains(child.Name), TextColor = theme.TextColor }; } objectChecks.Add((ICheckbox)selectWidget, child); rowContainer.AddChild(selectWidget); var checkBox = selectWidget as ICheckbox; checkBox.CheckedStateChanged += (s, e) => { if (s is ICheckbox checkbox) { if (checkBox.Checked) { if (!childSelector.Contains(objectChecks[checkbox].Name)) { childSelector.Add(objectChecks[checkbox].Name); } } else { if (childSelector.Contains(objectChecks[checkbox].Name)) { childSelector.Remove(objectChecks[checkbox].Name); } } } }; tabContainer.AddChild(rowContainer); } return(tabContainer); }