Changed to IComponentObject3D

Fixed a crash in convex hull
Improving path editor widget
Added x only pinch to radial pinch object
This commit is contained in:
MatterHackers 2023-11-12 21:16:03 -08:00
parent 3a0dd553d4
commit c4b58c9d39
13 changed files with 555 additions and 490 deletions

View file

@ -219,19 +219,19 @@ namespace MatterHackers.MatterControl
return new SceneOperation("EditComponent") return new SceneOperation("EditComponent")
{ {
TitleGetter = () => "Edit Component".Localize(), TitleGetter = () => "Edit Component".Localize(),
ResultType = typeof(ComponentObject3D), ResultType = typeof(IComponentObject3D),
Action = (sceneContext) => Action = (sceneContext) =>
{ {
var scene = sceneContext.Scene; var scene = sceneContext.Scene;
var sceneItem = scene.SelectedItem; var sceneItem = scene.SelectedItem;
if (sceneItem is ComponentObject3D componentObject) if (sceneItem is IComponentObject3D componentObject)
{ {
// Enable editing mode // Enable editing mode
componentObject.Finalized = false; componentObject.Finalized = false;
// Force editor rebuild // Force editor rebuild
scene.SelectedItem = null; scene.SelectedItem = null;
scene.SelectedItem = componentObject; scene.SelectedItem = componentObject as Object3D;
scene.Invalidate(new InvalidateArgs(null, InvalidateType.Children)); scene.Invalidate(new InvalidateArgs(null, InvalidateType.Children));
} }
@ -242,13 +242,13 @@ namespace MatterHackers.MatterControl
var sceneItem = scene.SelectedItem; var sceneItem = scene.SelectedItem;
return sceneItem.Parent != null return sceneItem.Parent != null
&& sceneItem.Parent.Parent == null && sceneItem.Parent.Parent == null
&& sceneItem is ComponentObject3D componentObject && sceneItem is IComponentObject3D componentObject
&& componentObject.Finalized && componentObject.Finalized
&& !componentObject.ProOnly; && !componentObject.ProOnly;
}, },
Icon = (theme) => StaticData.Instance.LoadIcon("scale_32x32.png", 16, 16).GrayToColor(theme.TextColor).SetPreMultiply(), Icon = (theme) => StaticData.Instance.LoadIcon("scale_32x32.png", 16, 16).GrayToColor(theme.TextColor).SetPreMultiply(),
HelpTextGetter = () => "A component must be selected".Localize().Stars(), HelpTextGetter = () => "A component must be selected".Localize().Stars(),
IsEnabled = (sceneContext) => sceneContext.Scene.SelectedItem != null && (sceneContext.Scene.SelectedItem is ComponentObject3D), IsEnabled = (sceneContext) => sceneContext.Scene.SelectedItem != null && (sceneContext.Scene.SelectedItem is IComponentObject3D),
}; };
} }
@ -274,7 +274,7 @@ namespace MatterHackers.MatterControl
return new SceneOperation("ImageConverter") return new SceneOperation("ImageConverter")
{ {
TitleGetter = () => "Image Converter".Localize(), TitleGetter = () => "Image Converter".Localize(),
ResultType = typeof(ComponentObject3D), ResultType = typeof(IComponentObject3D),
Action = (sceneContext) => Action = (sceneContext) =>
{ {
var scene = sceneContext.Scene; var scene = sceneContext.Scene;

View file

@ -198,7 +198,7 @@ namespace MatterHackers.MatterControl.DesignTools
} }
} }
public static IEnumerable<int> GetComponentExpressions(ComponentObject3D component, string checkForString, bool startsWith) public static IEnumerable<int> GetComponentExpressions(IComponentObject3D component, string checkForString, bool startsWith)
{ {
for (var i = 0; i < component.SurfacedEditors.Count; i++) for (var i = 0; i < component.SurfacedEditors.Count; i++)
{ {
@ -239,7 +239,7 @@ namespace MatterHackers.MatterControl.DesignTools
foreach (var item in itemToCheck.DescendantsAndSelf()) foreach (var item in itemToCheck.DescendantsAndSelf())
{ {
if (GetActiveExpression(item, checkForString, startsWith).Any() if (GetActiveExpression(item, checkForString, startsWith).Any()
|| itemToCheck is ComponentObject3D component || itemToCheck is IComponentObject3D component
&& GetComponentExpressions(component, checkForString, startsWith).Any()) && GetComponentExpressions(component, checkForString, startsWith).Any())
{ {
// three is one so return true // three is one so return true

View file

@ -58,7 +58,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
} }
// Also convert index expressions in ComponentObjects to their constants // Also convert index expressions in ComponentObjects to their constants
if (item is ComponentObject3D component) if (item is IComponentObject3D component)
{ {
for (int i = 0; i < component.SurfacedEditors.Count; i++) for (int i = 0; i < component.SurfacedEditors.Count; i++)
{ {
@ -105,7 +105,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
// we don't need to rebuild our source object // we don't need to rebuild our source object
return false; return false;
} }
else if (item.Parent is ComponentObject3D) else if (item.Parent is IComponentObject3D)
{ {
return false; return false;
} }

View file

@ -46,6 +46,10 @@ namespace MatterHackers.MatterControl.DesignTools
foreach (var visibleMesh in visibleMeshes) foreach (var visibleMesh in visibleMeshes)
{ {
var matrix = visibleMesh.source.WorldMatrix(object3D); var matrix = visibleMesh.source.WorldMatrix(object3D);
if (visibleMesh.convexHull == null)
{
continue;
}
foreach (var positon in visibleMesh.convexHull.Vertices) foreach (var positon in visibleMesh.convexHull.Vertices)
{ {
var transformed = positon.Transform(matrix); var transformed = positon.Transform(matrix);

View file

@ -68,6 +68,9 @@ namespace MatterHackers.MatterControl.DesignTools
private VertexStorage vertexStorage; private VertexStorage vertexStorage;
// the last vertex storage before the last change
private VertexStorage beforeLastChange;
private ControlPointConstraint controlPointConstraint = ControlPointConstraint.Free; private ControlPointConstraint controlPointConstraint = ControlPointConstraint.Free;
public PathEditorWidget(VertexStorage vertexStorage, public PathEditorWidget(VertexStorage vertexStorage,
@ -92,6 +95,7 @@ namespace MatterHackers.MatterControl.DesignTools
this.unscaledRenderOffset = unscaledRenderOffset; this.unscaledRenderOffset = unscaledRenderOffset;
this.layerScale = layerScale; this.layerScale = layerScale;
this.undoBuffer = undoBuffer;
var topToBottom = this; var topToBottom = this;
@ -105,6 +109,8 @@ namespace MatterHackers.MatterControl.DesignTools
this.vertexChanged = vertexChanged; this.vertexChanged = vertexChanged;
this.theme = theme; this.theme = theme;
this.vertexStorage = vertexStorage; this.vertexStorage = vertexStorage;
this.beforeLastChange = new VertexStorage();
beforeLastChange.SvgDString = vertexStorage.SvgDString;
var toolBar = new FlowLayoutWidget() var toolBar = new FlowLayoutWidget()
{ {
@ -148,7 +154,7 @@ namespace MatterHackers.MatterControl.DesignTools
var oldPosition = vertexStorage[controlPointBeingDragged].Position; var oldPosition = vertexStorage[controlPointBeingDragged].Position;
var newPosition = new Vector2(xEditWidget.ActuallNumberEdit.Value, yEditWidget.ActuallNumberEdit.Value); var newPosition = new Vector2(xEditWidget.ActuallNumberEdit.Value, yEditWidget.ActuallNumberEdit.Value);
var delta = newPosition - oldPosition; var delta = newPosition - oldPosition;
OffsetSelectedPoint(delta); OffsetSelectedPoint(delta, true);
}; };
xEditWidget.ActuallNumberEdit.KeyDown += NumberField.InternalTextEditWidget_KeyDown; xEditWidget.ActuallNumberEdit.KeyDown += NumberField.InternalTextEditWidget_KeyDown;
@ -168,7 +174,7 @@ namespace MatterHackers.MatterControl.DesignTools
var oldPosition = vertexStorage[controlPointBeingDragged].Position; var oldPosition = vertexStorage[controlPointBeingDragged].Position;
var newPosition = new Vector2(xEditWidget.ActuallNumberEdit.Value, yEditWidget.ActuallNumberEdit.Value); var newPosition = new Vector2(xEditWidget.ActuallNumberEdit.Value, yEditWidget.ActuallNumberEdit.Value);
var delta = newPosition - oldPosition; var delta = newPosition - oldPosition;
OffsetSelectedPoint(delta); OffsetSelectedPoint(delta, true);
}; };
yEditWidget.ActuallNumberEdit.KeyDown += NumberField.InternalTextEditWidget_KeyDown; yEditWidget.ActuallNumberEdit.KeyDown += NumberField.InternalTextEditWidget_KeyDown;
@ -234,6 +240,8 @@ namespace MatterHackers.MatterControl.DesignTools
public ETransformState TransformState { get; set; } public ETransformState TransformState { get; set; }
private double layerScale { get; set; } = 1; private double layerScale { get; set; } = 1;
private UndoBuffer undoBuffer;
private Affine ScalingTransform => Affine.NewScaling(layerScale, layerScale); private Affine ScalingTransform => Affine.NewScaling(layerScale, layerScale);
private Affine TotalTransform => Affine.NewTranslation(unscaledRenderOffset) * ScalingTransform * Affine.NewTranslation(Width / 2, Height / 2); private Affine TotalTransform => Affine.NewTranslation(unscaledRenderOffset) * ScalingTransform * Affine.NewTranslation(Width / 2, Height / 2);
@ -246,6 +254,7 @@ namespace MatterHackers.MatterControl.DesignTools
private ThemedRadioTextButton sharpButton; private ThemedRadioTextButton sharpButton;
private ThemedRadioTextButton alignedButton; private ThemedRadioTextButton alignedButton;
private ThemedRadioTextButton freeButton; private ThemedRadioTextButton freeButton;
private bool hasBeenStartupPositioned;
public void CenterPartInView() public void CenterPartInView()
{ {
@ -254,15 +263,29 @@ namespace MatterHackers.MatterControl.DesignTools
var partBounds = vertexStorage.GetBounds(); var partBounds = vertexStorage.GetBounds();
var weightedCenter = partBounds.Center; var weightedCenter = partBounds.Center;
var bottomPixels = 20;
var margin = 30;
unscaledRenderOffset = -weightedCenter; unscaledRenderOffset = -weightedCenter;
layerScale = Math.Min((Height - 30) / partBounds.Height, (Width - 30) / partBounds.Width); layerScale = Math.Min((Height - margin - bottomPixels * 2) / partBounds.Height, (Width - margin) / partBounds.Width);
unscaledRenderOffset += new Vector2(0, bottomPixels) / layerScale;
Invalidate(); Invalidate();
scaleChanged?.Invoke(unscaledRenderOffset, layerScale);
} }
} }
public override void OnDraw(Graphics2D graphics2D) public override void OnDraw(Graphics2D graphics2D)
{ {
if (!hasBeenStartupPositioned)
{
if (unscaledRenderOffset == Vector2.Zero
&& layerScale == 1)
{
CenterPartInView();
}
hasBeenStartupPositioned = true;
}
new VertexSourceApplyTransform(vertexStorage, TotalTransform).RenderPath(graphics2D, theme.TextColor, 2, true, theme.PrimaryAccentColor.Blend(theme.TextColor, .5), theme.PrimaryAccentColor); new VertexSourceApplyTransform(vertexStorage, TotalTransform).RenderPath(graphics2D, theme.TextColor, 2, true, theme.PrimaryAccentColor.Blend(theme.TextColor, .5), theme.PrimaryAccentColor);
//if (vertexStorage.GetType().GetCustomAttributes(typeof(PathEditorFactory.ShowAxisAttribute), true).FirstOrDefault() is PathEditorFactory.ShowAxisAttribute showAxisAttribute) //if (vertexStorage.GetType().GetCustomAttributes(typeof(PathEditorFactory.ShowAxisAttribute), true).FirstOrDefault() is PathEditorFactory.ShowAxisAttribute showAxisAttribute)
@ -412,7 +435,7 @@ namespace MatterHackers.MatterControl.DesignTools
if (mouseDelta.LengthSquared > 0) if (mouseDelta.LengthSquared > 0)
{ {
ScalingTransform.inverse_transform(ref mouseDelta); ScalingTransform.inverse_transform(ref mouseDelta);
OffsetSelectedPoint(mouseDelta); OffsetSelectedPoint(mouseDelta, false);
UpdateControlsForSelection(); UpdateControlsForSelection();
} }
} }
@ -425,38 +448,67 @@ namespace MatterHackers.MatterControl.DesignTools
lastMousePosition = mouseEvent.Position; lastMousePosition = mouseEvent.Position;
} }
private void OffsetSelectedPoint(Vector2 delta) public override void OnMouseUp(MouseEventArgs mouseEvent)
{
OffsetSelectedPoint(new Vector2(), true);
base.OnMouseUp(mouseEvent);
}
private void OffsetSelectedPoint(Vector2 delta, bool recordUndo)
{ {
if (controlPointBeingDragged < 0 if (controlPointBeingDragged < 0
|| controlPointBeingDragged >= vertexStorage.Count || controlPointBeingDragged >= vertexStorage.Count)
|| delta.LengthSquared == 0)
{ {
return; return;
} }
var vertexData = vertexStorage[controlPointBeingDragged]; if (delta.LengthSquared > 0)
if (vertexData.Hint == CommandHint.C4Point)
{ {
for (int i = -1; i < 2; i++) var vertexData = vertexStorage[controlPointBeingDragged];
if (vertexData.Hint == CommandHint.C4Point)
{ {
var pointIndex = controlPointBeingDragged + i; for (int i = -1; i < 2; i++)
// the prev point
if (pointIndex > 0
&& pointIndex < vertexStorage.Count)
{ {
var vertexData2 = vertexStorage[pointIndex]; var pointIndex = controlPointBeingDragged + i;
vertexStorage[pointIndex] = new VertexData(vertexData2.Command, vertexData2.Position + delta, vertexData2.Hint); // the prev point
if (pointIndex > 0
&& pointIndex < vertexStorage.Count)
{
var vertexData2 = vertexStorage[pointIndex];
vertexStorage[pointIndex] = new VertexData(vertexData2.Command, vertexData2.Position + delta, vertexData2.Hint);
}
} }
} }
} else
else {
{ // only drag the point
// only drag the point vertexStorage[controlPointBeingDragged] = new VertexData(vertexData.Command, vertexData.Position + delta, vertexData.Hint);
vertexStorage[controlPointBeingDragged] = new VertexData(vertexData.Command, vertexData.Position + delta, vertexData.Hint); }
vertexChanged?.Invoke();
} }
vertexChanged?.Invoke(); if (recordUndo)
{
var doVertexBuffer = new VertexStorage();
doVertexBuffer.SvgDString = vertexStorage.SvgDString;
var undoVertexBuffer = new VertexStorage();
undoVertexBuffer.SvgDString = beforeLastChange.SvgDString;
undoBuffer.AddAndDo(new UndoRedoActions(() =>
{
vertexStorage.SvgDString = undoVertexBuffer.SvgDString;
vertexChanged?.Invoke();
}, () =>
{
vertexStorage.SvgDString = doVertexBuffer.SvgDString;
vertexChanged?.Invoke();
}));
// record the change
beforeLastChange.SvgDString = vertexStorage.SvgDString;
}
} }
private void DoTranslateAndZoom(MouseEventArgs mouseEvent) private void DoTranslateAndZoom(MouseEventArgs mouseEvent)

View file

@ -65,9 +65,15 @@ namespace MatterHackers.MatterControl.DesignTools
[Slider(0, 50, snapDistance: 1)] [Slider(0, 50, snapDistance: 1)]
public IntOrExpression PinchSlices { get; set; } = 20; public IntOrExpression PinchSlices { get; set; } = 20;
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)]
[Description("Enable advanced features.")] [Description("Enable advanced features.")]
public bool Advanced { get; set; } = false; public bool Advanced { get; set; } = false;
public enum PinchType { Radial, XAxis }
public PinchType PinchTypeValue { get; set; } = PinchType.Radial;
[Description("Allows for the repositioning of the rotation origin")] [Description("Allows for the repositioning of the rotation origin")]
public Vector2 RotationOffset { get; set; } public Vector2 RotationOffset { get; set; }
@ -203,15 +209,11 @@ namespace MatterHackers.MatterControl.DesignTools
} }
var positionXy = new Vector2(position) - rotationCenter; var positionXy = new Vector2(position) - rotationCenter;
var fromLine = true; positionXy *= horizontalOffset.GetXAtY(position.Z * 10) / (maxRadius * 10);
if (fromLine) if (PinchTypeValue == PinchType.XAxis)
{ {
positionXy *= horizontalOffset.GetXAtY(position.Z * 10) / (maxRadius * 10); // only use the x value
//positionXy *= xAtYInterpolator.Get(position.Z * 10) / maxRadius; positionXy.Y = position.Y;
}
else
{
positionXy *= Easing.Quadratic.InOut(ratio);
} }
positionXy += rotationCenter; positionXy += rotationCenter;
transformedMesh.Vertices[i] = new Vector3Float(positionXy.X, positionXy.Y, position.Z); transformedMesh.Vertices[i] = new Vector3Float(positionXy.X, positionXy.Y, position.Z);

View file

@ -27,275 +27,284 @@ of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project. either expressed or implied, of the FreeBSD Project.
*/ */
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using MatterHackers.Agg.UI; using MatterHackers.Agg.UI;
using MatterHackers.DataConverters3D; using MatterHackers.DataConverters3D;
using MatterHackers.DataConverters3D.UndoCommands; using MatterHackers.DataConverters3D.UndoCommands;
using MatterHackers.Localizations; using MatterHackers.Localizations;
using MatterHackers.MatterControl.DesignTools.Operations; using MatterHackers.MatterControl.DesignTools.Operations;
using MatterHackers.MatterControl.PartPreviewWindow; using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading;
namespace MatterHackers.MatterControl.DesignTools namespace MatterHackers.MatterControl.DesignTools
{ {
[HideChildrenFromTreeView] public interface IComponentObject3D : IObject3D
public class ComponentObject3D : Object3D, IRightClickMenuProvider {
{ bool Finalized { get; set; }
private const string ImageConverterComponentID = "4D9BD8DB-C544-4294-9C08-4195A409217A"; bool ProOnly { get; set; }
public ComponentObject3D() (string cellId, string cellData) DecodeContent(int editorIndex);
{
}
public ComponentObject3D(IEnumerable<IObject3D> children) List<string> SurfacedEditors { get; set; }
: base(children) }
{
}
public override bool CanApply => !Finalized || Persistable; [HideChildrenFromTreeView]
public class ComponentObject3D : Object3D, IRightClickMenuProvider, IComponentObject3D
{
private const string ImageConverterComponentID = "4D9BD8DB-C544-4294-9C08-4195A409217A";
public override bool Persistable => ApplicationController.Instance.UserHasPermission(this); public ComponentObject3D()
private bool _finalizade = true;
[Description("Switch from editing to distribution")]
public bool Finalized
{
get => _finalizade;
set
{
_finalizade = value;
// on any invalidate ensure that the visibility setting are correct for embedded sheet objects
foreach (var child in this.Descendants())
{
if (child is SheetObject3D)
{
child.Visible = !this.Finalized;
}
}
}
}
public List<string> SurfacedEditors { get; set; } = new List<string>();
[HideFromEditor]
public string ComponentID { get; set; } = "";
[Description("MatterHackers Internal Use")]
public bool ProOnly { get; set; }
public override void Apply(UndoBuffer undoBuffer)
{
// we want to end up with just a group of all the visible mesh objects
using (RebuildLock())
{
var newChildren = new List<IObject3D>();
// push our matrix into a copy of our visible children
foreach (var child in this.VisibleMeshes())
{
var meshOnlyItem = new Object3D
{
Matrix = child.WorldMatrix(this),
Color = child.WorldColor(this),
MaterialIndex = child.WorldMaterialIndex(this),
OutputType = child.WorldOutputType(this),
Mesh = child.Mesh,
Name = "Mesh".Localize()
};
newChildren.Add(meshOnlyItem);
}
if (newChildren.Count > 1)
{
var group = new GroupHolesAppliedObject3D
{
Name = this.Name
};
group.Children.Modify(list =>
{
list.AddRange(newChildren);
});
newChildren.Clear();
newChildren.Add(group);
}
else if (newChildren.Count == 1)
{
newChildren[0].Name = this.Name;
}
// and replace us with the children
undoBuffer.AddAndDo(new ReplaceCommand(new[] { this }, newChildren));
}
Invalidate(InvalidateType.Children);
}
public (string cellId, string cellData) DecodeContent(int editorIndex)
{
if (SurfacedEditors[editorIndex].StartsWith("!"))
{
var cellData2 = SurfacedEditors[editorIndex].Substring(1);
var cellId2 = cellData2.ToLower();
// check if it has embededdata
var separator = cellData2.IndexOf(',');
if (separator != -1)
{
cellId2 = cellData2.Substring(0, separator).ToLower();
cellData2 = cellData2.Substring(separator + 1);
}
else
{
var controledSheet = ControledSheet;
if (controledSheet != null)
{
// We don't have any cache of the cell content, get the current content
cellData2 = controledSheet.SheetData.GetCellValue(cellId2);
}
}
return (cellId2, cellData2);
}
return (null, null);
}
private SheetObject3D ControledSheet => this.Descendants<SheetObject3D>().ToArray()[0];//.FirstOrDefault();
// this.Children.Where(s => s is SheetObject3D).FirstOrDefault() as SheetObject3D;
private void RecalculateSheet()
{
// if there are editors that reference cells
for (int i=0; i<SurfacedEditors.Count; i++)
{
var (cellId, cellData) = this.DecodeContent(i);
if (cellData != null
&& cellData.StartsWith("="))
{
var expression = new DoubleOrExpression(cellData);
var controledSheet = ControledSheet;
if (controledSheet != null)
{
var cell = controledSheet.SheetData[cellId];
if (cell != null)
{
cell.Expression = expression.Value(this).ToString();
}
}
}
}
if (SurfacedEditors.Any(se => se.StartsWith("!"))
&& !this.RebuildLocked)
{
var controledSheet = ControledSheet;
var componentLock = this.RebuildLock();
controledSheet.SheetData.Recalculate();
UiThread.RunOnIdle(() =>
{
// wait until the sheet is done rebuilding (or 30 seconds)
var startTime = UiThread.CurrentTimerMs;
while (controledSheet.RebuildLocked
&& startTime + 30000 < UiThread.CurrentTimerMs)
{
Thread.Sleep(1);
}
componentLock.Dispose();
});
}
}
public override void OnInvalidate(InvalidateArgs invalidateType)
{ {
switch(invalidateType.InvalidateType) }
public ComponentObject3D(IEnumerable<IObject3D> children)
: base(children)
{
}
public override bool CanApply => !Finalized || Persistable;
public override bool Persistable => ApplicationController.Instance.UserHasPermission(this);
private bool _finalizade = true;
[Description("Switch from editing to distribution")]
public bool Finalized
{
get => _finalizade;
set
{ {
case InvalidateType.SheetUpdated: _finalizade = value;
case InvalidateType.Properties: // on any invalidate ensure that the visibility setting are correct for embedded sheet objects
RecalculateSheet(); foreach (var child in this.Descendants())
break; {
if (child is SheetObject3D)
{
child.Visible = !this.Finalized;
}
}
}
}
public List<string> SurfacedEditors { get; set; } = new List<string>();
[HideFromEditor]
public string ComponentID { get; set; } = "";
[Description("MatterHackers Internal Use")]
public bool ProOnly { get; set; }
public override void Apply(UndoBuffer undoBuffer)
{
// we want to end up with just a group of all the visible mesh objects
using (RebuildLock())
{
var newChildren = new List<IObject3D>();
// push our matrix into a copy of our visible children
foreach (var child in this.VisibleMeshes())
{
var meshOnlyItem = new Object3D
{
Matrix = child.WorldMatrix(this),
Color = child.WorldColor(this),
MaterialIndex = child.WorldMaterialIndex(this),
OutputType = child.WorldOutputType(this),
Mesh = child.Mesh,
Name = "Mesh".Localize()
};
newChildren.Add(meshOnlyItem);
}
if (newChildren.Count > 1)
{
var group = new GroupHolesAppliedObject3D
{
Name = this.Name
};
group.Children.Modify(list =>
{
list.AddRange(newChildren);
});
newChildren.Clear();
newChildren.Add(group);
}
else if (newChildren.Count == 1)
{
newChildren[0].Name = this.Name;
}
// and replace us with the children
undoBuffer.AddAndDo(new ReplaceCommand(new[] { this }, newChildren));
}
Invalidate(InvalidateType.Children);
}
public (string cellId, string cellData) DecodeContent(int editorIndex)
{
if (SurfacedEditors[editorIndex].StartsWith("!"))
{
var cellData2 = SurfacedEditors[editorIndex].Substring(1);
var cellId2 = cellData2.ToLower();
// check if it has embededdata
var separator = cellData2.IndexOf(',');
if (separator != -1)
{
cellId2 = cellData2.Substring(0, separator).ToLower();
cellData2 = cellData2.Substring(separator + 1);
}
else
{
var controledSheet = ControledSheet;
if (controledSheet != null)
{
// We don't have any cache of the cell content, get the current content
cellData2 = controledSheet.SheetData.GetCellValue(cellId2);
}
}
return (cellId2, cellData2);
}
return (null, null);
}
private SheetObject3D ControledSheet => this.Descendants<SheetObject3D>().ToArray()[0];//.FirstOrDefault();
// this.Children.Where(s => s is SheetObject3D).FirstOrDefault() as SheetObject3D;
private void RecalculateSheet()
{
// if there are editors that reference cells
for (int i = 0; i < SurfacedEditors.Count; i++)
{
var (cellId, cellData) = this.DecodeContent(i);
if (cellData != null
&& cellData.StartsWith("="))
{
var expression = new DoubleOrExpression(cellData);
var controledSheet = ControledSheet;
if (controledSheet != null)
{
var cell = controledSheet.SheetData[cellId];
if (cell != null)
{
cell.Expression = expression.Value(this).ToString();
}
}
}
}
if (SurfacedEditors.Any(se => se.StartsWith("!"))
&& !this.RebuildLocked)
{
var controledSheet = ControledSheet;
var componentLock = this.RebuildLock();
controledSheet.SheetData.Recalculate();
UiThread.RunOnIdle(() =>
{
// wait until the sheet is done rebuilding (or 30 seconds)
var startTime = UiThread.CurrentTimerMs;
while (controledSheet.RebuildLocked
&& startTime + 30000 < UiThread.CurrentTimerMs)
{
Thread.Sleep(1);
}
componentLock.Dispose();
});
}
}
public override void OnInvalidate(InvalidateArgs invalidateType)
{
switch (invalidateType.InvalidateType)
{
case InvalidateType.SheetUpdated:
case InvalidateType.Properties:
RecalculateSheet();
break;
} }
base.OnInvalidate(invalidateType); base.OnInvalidate(invalidateType);
} }
public override void Cancel(UndoBuffer undoBuffer) public override void Cancel(UndoBuffer undoBuffer)
{ {
// Make any hiden children visible // Make any hiden children visible
// on any invalidate ensure that the visibility setting are correct for embedded sheet objects // on any invalidate ensure that the visibility setting are correct for embedded sheet objects
foreach (var child in this.Descendants()) foreach (var child in this.Descendants())
{ {
if (child is SheetObject3D) if (child is SheetObject3D)
{ {
child.Visible = true; child.Visible = true;
} }
} }
// Custom remove for ImageConverter // Custom remove for ImageConverter
if (this.ComponentID == ImageConverterComponentID) if (this.ComponentID == ImageConverterComponentID)
{ {
var parent = this.Parent; var parent = this.Parent;
using (RebuildLock()) using (RebuildLock())
{ {
if (this.Descendants<ImageObject3D>().FirstOrDefault() is ImageObject3D imageObject3D) if (this.Descendants<ImageObject3D>().FirstOrDefault() is ImageObject3D imageObject3D)
{ {
imageObject3D.Matrix = this.Matrix; imageObject3D.Matrix = this.Matrix;
if (undoBuffer != null) if (undoBuffer != null)
{ {
undoBuffer.AddAndDo(new ReplaceCommand(new[] { this }, new[] { imageObject3D })); undoBuffer.AddAndDo(new ReplaceCommand(new[] { this }, new[] { imageObject3D }));
} }
else else
{ {
parent.Children.Modify(list => parent.Children.Modify(list =>
{ {
list.Remove(this); list.Remove(this);
list.Add(imageObject3D); list.Add(imageObject3D);
}); });
} }
} }
} }
parent.Invalidate(new InvalidateArgs(this, InvalidateType.Children)); parent.Invalidate(new InvalidateArgs(this, InvalidateType.Children));
} }
else else
{ {
if (ProOnly) if (ProOnly)
{ {
// just delete it // just delete it
var parent = this.Parent; var parent = this.Parent;
using (RebuildLock()) using (RebuildLock())
{ {
if (undoBuffer != null) if (undoBuffer != null)
{ {
// and replace us with nothing // and replace us with nothing
undoBuffer.AddAndDo(new ReplaceCommand(new[] { this }, new List<IObject3D>(), false)); undoBuffer.AddAndDo(new ReplaceCommand(new[] { this }, new List<IObject3D>(), false));
} }
else else
{ {
parent.Children.Modify(list => parent.Children.Modify(list =>
{ {
list.Remove(this); list.Remove(this);
}); });
} }
} }
parent.Invalidate(new InvalidateArgs(this, InvalidateType.Children)); parent.Invalidate(new InvalidateArgs(this, InvalidateType.Children));
} }
else else
{ {
// remove the component and leave the inside parts // remove the component and leave the inside parts
base.Cancel(undoBuffer); base.Cancel(undoBuffer);
} }
} }
} }
public void AddRightClickMenuItemsItems(PopupMenu popupMenu, ThemeConfig theme) public void AddRightClickMenuItemsItems(PopupMenu popupMenu, ThemeConfig theme)
{ {
@ -303,20 +312,20 @@ namespace MatterHackers.MatterControl.DesignTools
string componentID = this.ComponentID; string componentID = this.ComponentID;
var helpItem = popupMenu.CreateMenuItem("Help".Localize()); var helpItem = popupMenu.CreateMenuItem("Help".Localize());
var helpArticlesByID = ApplicationController.Instance.HelpArticlesByID; var helpArticlesByID = ApplicationController.Instance.HelpArticlesByID;
helpItem.Enabled = !string.IsNullOrEmpty(componentID) && helpArticlesByID.ContainsKey(componentID); helpItem.Enabled = !string.IsNullOrEmpty(componentID) && helpArticlesByID.ContainsKey(componentID);
helpItem.Click += (s, e) => helpItem.Click += (s, e) =>
{ {
var helpTab = ApplicationController.Instance.ActivateHelpTab("Docs"); var helpTab = ApplicationController.Instance.ActivateHelpTab("Docs");
if (helpTab.TabContent is HelpTreePanel helpTreePanel) if (helpTab.TabContent is HelpTreePanel helpTreePanel)
{ {
if (helpArticlesByID.TryGetValue(componentID, out HelpArticle helpArticle)) if (helpArticlesByID.TryGetValue(componentID, out HelpArticle helpArticle))
{ {
helpTreePanel.ActiveNodePath = componentID; helpTreePanel.ActiveNodePath = componentID;
} }
} }
}; };
} }
} }
} }

View file

@ -29,7 +29,6 @@ either expressed or implied, of the FreeBSD Project.
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using MatterHackers.Agg;
using MatterHackers.Agg.VertexSource; using MatterHackers.Agg.VertexSource;
using MatterHackers.DataConverters3D; using MatterHackers.DataConverters3D;
using MatterHackers.Localizations; using MatterHackers.Localizations;

View file

@ -1,5 +1,5 @@
/* /*
Copyright (c) 2018, Lars Brubaker, John Lewin Copyright (c) 2023, Lars Brubaker, John Lewin
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@ -27,225 +27,222 @@ of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project. either expressed or implied, of the FreeBSD Project.
*/ */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MatterHackers.Agg; using MatterHackers.Agg;
using MatterHackers.Agg.Image;
using MatterHackers.Agg.UI; using MatterHackers.Agg.UI;
using MatterHackers.DataConverters3D; using MatterHackers.DataConverters3D;
using MatterHackers.Localizations; using MatterHackers.Localizations;
using MatterHackers.MatterControl.CustomWidgets;
using MatterHackers.MatterControl.DesignTools; using MatterHackers.MatterControl.DesignTools;
using MatterHackers.MatterControl.DesignTools.Operations; using MatterHackers.MatterControl.DesignTools.Operations;
using MatterHackers.MatterControl.Library; using MatterHackers.MatterControl.Library;
using MatterHackers.MatterControl.PartPreviewWindow.View3D; using MatterHackers.MatterControl.PartPreviewWindow.View3D;
using System.Collections.Generic;
using System.Linq;
namespace MatterHackers.MatterControl.PartPreviewWindow namespace MatterHackers.MatterControl.PartPreviewWindow
{ {
public static class Object3DTreeBuilder public static class Object3DTreeBuilder
{ {
public static TreeNode BuildTree(IObject3D rootItem, Dictionary<IObject3D, TreeNode> keyValues, ThemeConfig theme) public static TreeNode BuildTree(IObject3D rootItem, Dictionary<IObject3D, TreeNode> keyValues, ThemeConfig theme)
{ {
return AddTree(BuildItemView(rootItem), null, keyValues, theme); return AddTree(BuildItemView(rootItem), null, keyValues, theme);
} }
private static TreeNode AddTree(ObjectView objectView, TreeNode parentTreeNode, Dictionary<IObject3D, TreeNode> keyValues, ThemeConfig theme) private static TreeNode AddTree(ObjectView objectView, TreeNode parentTreeNode, Dictionary<IObject3D, TreeNode> keyValues, ThemeConfig theme)
{ {
// Suppress MeshWrapper and OperationSource nodes in tree // Suppress MeshWrapper and OperationSource nodes in tree
bool shouldCollapseToParent = objectView.Source is ModifiedMeshObject3D || objectView.Source is OperationSourceObject3D; bool shouldCollapseToParent = objectView.Source is ModifiedMeshObject3D || objectView.Source is OperationSourceObject3D;
TreeNode contextNode; TreeNode contextNode;
if (shouldCollapseToParent
&& parentTreeNode != null
&& !keyValues.ContainsKey(objectView.Source))
{
contextNode = parentTreeNode;
}
else
{
var itemName = GetName(objectView);
contextNode = AddItem(objectView.Source, itemName, parentTreeNode, keyValues, theme);
}
using (contextNode.LayoutLock()) if (shouldCollapseToParent
{ && parentTreeNode != null
var componentObject3D = objectView.Source as ComponentObject3D; && !keyValues.ContainsKey(objectView.Source))
var hideChildren = objectView.Source.GetType().GetCustomAttributes(typeof(HideChildrenFromTreeViewAttribute), true).Any(); {
contextNode = parentTreeNode;
}
else
{
var itemName = GetName(objectView);
contextNode = AddItem(objectView.Source, itemName, parentTreeNode, keyValues, theme);
}
if ((componentObject3D?.Finalized == false using (contextNode.LayoutLock())
|| !hideChildren) {
&& objectView.Children?.Any() == true) var componentObject3D = objectView.Source as IComponentObject3D;
{ var hideChildren = objectView.Source.GetType().GetCustomAttributes(typeof(HideChildrenFromTreeViewAttribute), true).Any();
var orderChildrenByIndex = objectView.Source.GetType().GetCustomAttributes(typeof(OrderChildrenByIndexAttribute), true).Any();
IEnumerable<IObject3D> children = objectView.Children;
if (!orderChildrenByIndex) if ((componentObject3D?.Finalized == false
{ || !hideChildren)
children = objectView.Children.OrderBy(i => i.Name); && objectView.Children?.Any() == true)
} {
foreach (var child in children) var orderChildrenByIndex = objectView.Source.GetType().GetCustomAttributes(typeof(OrderChildrenByIndexAttribute), true).Any();
{ IEnumerable<IObject3D> children = objectView.Children;
if (child != null
&& !child.GetType().GetCustomAttributes(typeof(HideFromTreeViewAttribute), true).Any())
{
AddTree(BuildItemView(child), contextNode, keyValues, theme);
}
}
}
}
return contextNode; if (!orderChildrenByIndex)
} {
children = objectView.Children.OrderBy(i => i.Name);
}
foreach (var child in children)
{
if (child != null
&& !child.GetType().GetCustomAttributes(typeof(HideFromTreeViewAttribute), true).Any())
{
AddTree(BuildItemView(child), contextNode, keyValues, theme);
}
}
}
}
private static TreeNode AddItem(IObject3D item, string itemName, TreeNode parentNode, Dictionary<IObject3D, TreeNode> keyValues, ThemeConfig theme) return contextNode;
{ }
if (item is InsertionGroupObject3D insertionGroup)
{
return new TreeNode(theme)
{
Text = "Loading".Localize(),
Tag = item,
TextColor = theme.TextColor,
PointSize = theme.DefaultFontSize,
};
}
var node = new TreeNode(theme) private static TreeNode AddItem(IObject3D item, string itemName, TreeNode parentNode, Dictionary<IObject3D, TreeNode> keyValues, ThemeConfig theme)
{ {
Text = itemName, if (item is InsertionGroupObject3D insertionGroup)
Tag = item, {
TextColor = theme.TextColor, return new TreeNode(theme)
PointSize = theme.DefaultFontSize, {
}; Text = "Loading".Localize(),
Tag = item,
TextColor = theme.TextColor,
PointSize = theme.DefaultFontSize,
};
}
if (!keyValues.ContainsKey(item)) var node = new TreeNode(theme)
{ {
keyValues.Add(item, node); Text = itemName,
} Tag = item,
TextColor = theme.TextColor,
PointSize = theme.DefaultFontSize,
};
// Check for operation resulting in the given type if (!keyValues.ContainsKey(item))
var image = SceneOperations.GetIcon(item.GetType(), theme); {
keyValues.Add(item, node);
}
if (image != null) // Check for operation resulting in the given type
{ var image = SceneOperations.GetIcon(item.GetType(), theme);
node.Image = image;
}
else
{
node.Image = ApplicationController.Instance.Thumbnails.DefaultThumbnail();
node.Load += (s, e) => if (image != null)
{ {
string contentID = item.MeshRenderId().ToString(); node.Image = image;
if (item is IStaticThumbnail staticThumbnail) }
{ else
contentID = $"MatterHackers/ItemGenerator/{staticThumbnail.ThumbnailName}".GetLongHashCode().ToString(); {
} node.Image = ApplicationController.Instance.Thumbnails.DefaultThumbnail();
var thumbnail = ApplicationController.Instance.Thumbnails.LoadCachedImage(contentID, 16, 16); node.Load += (s, e) =>
{
string contentID = item.MeshRenderId().ToString();
if (item is IStaticThumbnail staticThumbnail)
{
contentID = $"MatterHackers/ItemGenerator/{staticThumbnail.ThumbnailName}".GetLongHashCode().ToString();
}
node.Image = thumbnail ?? ApplicationController.Instance.Thumbnails.DefaultThumbnail(); var thumbnail = ApplicationController.Instance.Thumbnails.LoadCachedImage(contentID, 16, 16);
};
}
if (parentNode != null) node.Image = thumbnail ?? ApplicationController.Instance.Thumbnails.DefaultThumbnail();
{ };
parentNode.Nodes.Add(node); }
if (parentNode.Tag is IObject3D object3D)
{
parentNode.Expanded = object3D.Expanded;
}
}
node.ExpandedChanged += (s, e) => if (parentNode != null)
{ {
if (item is Object3D object3D) parentNode.Nodes.Add(node);
{ if (parentNode.Tag is IObject3D object3D)
object3D.Expanded = node.Expanded; {
} parentNode.Expanded = object3D.Expanded;
}; }
}
return node; node.ExpandedChanged += (s, e) =>
} {
if (item is Object3D object3D)
{
object3D.Expanded = node.Expanded;
}
};
private static string GetName(ObjectView item) return node;
{ }
return !string.IsNullOrEmpty(item.Name) ? $"{item.Name}" : $"{item.GetType().Name}";
}
private static ObjectView BuildItemView(IObject3D item) private static string GetName(ObjectView item)
{ {
string GetArrayName(ArrayObject3D arrayItem) return !string.IsNullOrEmpty(item.Name) ? $"{item.Name}" : $"{item.GetType().Name}";
{ }
if (string.IsNullOrWhiteSpace(item.Name)
&& arrayItem?.SourceContainer?.Children?.Any() == true)
{
var childName = arrayItem.SourceContainer.Children.First().Name;
if (childName.Length > 20)
{
childName = childName.Substring(0, 20) + "...";
}
return $"{childName} - x{arrayItem.Count}"; private static ObjectView BuildItemView(IObject3D item)
} {
string GetArrayName(ArrayObject3D arrayItem)
{
if (string.IsNullOrWhiteSpace(item.Name)
&& arrayItem?.SourceContainer?.Children?.Any() == true)
{
var childName = arrayItem.SourceContainer.Children.First().Name;
if (childName.Length > 20)
{
childName = childName.Substring(0, 20) + "...";
}
return arrayItem.Name; return $"{childName} - x{arrayItem.Count}";
} }
if (item is ArrayObject3D array) return arrayItem.Name;
{ }
return new ObjectView()
{
Children = item.Children.OfType<OperationSourceObject3D>().ToList(),
Name = GetArrayName(array),
Source = item
};
}
else
{
switch (item)
{
case TransformWrapperObject3D transformWrapperObject3D:
return new ObjectView()
{
Children = transformWrapperObject3D.UntransformedChildren,
Name = item.Name,
Source = item
};
case OperationSourceContainerObject3D operationSourceContainerObject3D: if (item is ArrayObject3D array)
return new ObjectView() {
{ return new ObjectView()
Children = item.Children.OfType<OperationSourceObject3D>().ToList(), {
Name = operationSourceContainerObject3D.Name, Children = item.Children.OfType<OperationSourceObject3D>().ToList(),
Source = item Name = GetArrayName(array),
}; Source = item
};
}
else
{
switch (item)
{
case TransformWrapperObject3D transformWrapperObject3D:
return new ObjectView()
{
Children = transformWrapperObject3D.UntransformedChildren,
Name = item.Name,
Source = item
};
default: case OperationSourceContainerObject3D operationSourceContainerObject3D:
return new ObjectView(item); return new ObjectView()
} {
} Children = item.Children.OfType<OperationSourceObject3D>().ToList(),
} Name = operationSourceContainerObject3D.Name,
Source = item
};
private class ObjectView default:
{ return new ObjectView(item);
public ObjectView() }
{ }
} }
public ObjectView(IObject3D source) private class ObjectView
{ {
this.Source = source; public ObjectView()
this.Children = this.Source.Children; {
this.Name = this.Source.Name; }
}
public IEnumerable<IObject3D> Children { get; set; } public ObjectView(IObject3D source)
{
this.Source = source;
this.Children = this.Source.Children;
this.Name = this.Source.Name;
}
public string Name { get; set; } public IEnumerable<IObject3D> Children { get; set; }
public IObject3D Source { get; set; } public string Name { get; set; }
}
} public IObject3D Source { get; set; }
}
}
} }

View file

@ -304,7 +304,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
var rows = new SafeList<SettingsRow>(); var rows = new SafeList<SettingsRow>();
// put in the normal editor // put in the normal editor
if (selectedItem is ComponentObject3D componentObject if (selectedItem is IComponentObject3D componentObject
&& componentObject.Finalized) && componentObject.Finalized)
{ {
AddComponentEditor(selectedItem, undoBuffer, rows, componentObject, ref tabIndex); AddComponentEditor(selectedItem, undoBuffer, rows, componentObject, ref tabIndex);
@ -538,7 +538,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
} }
} }
private void AddComponentEditor(IObject3D selectedItem, UndoBuffer undoBuffer, SafeList<SettingsRow> rows, ComponentObject3D componentObject, ref int tabIndex) private void AddComponentEditor(IObject3D selectedItem, UndoBuffer undoBuffer, SafeList<SettingsRow> rows, IComponentObject3D componentObject, ref int tabIndex)
{ {
var context = new EditorContext(); var context = new EditorContext();
PropertyEditor.AddUnlockLinkIfRequired(selectedItem, editorPanel, theme); PropertyEditor.AddUnlockLinkIfRequired(selectedItem, editorPanel, theme);
@ -600,7 +600,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
} }
} }
private void AddSheetCellEditor(UndoBuffer undoBuffer, ComponentObject3D componentObject, List<string> editorList, int editorIndex) private void AddSheetCellEditor(UndoBuffer undoBuffer, IComponentObject3D componentObject, List<string> editorList, int editorIndex)
{ {
var firtSheet = componentObject.Descendants<SheetObject3D>().FirstOrDefault(); var firtSheet = componentObject.Descendants<SheetObject3D>().FirstOrDefault();
if (firtSheet != null) if (firtSheet != null)
@ -633,7 +633,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
editorList[editorIndex] = "!" + cellId + "," + oldValue; editorList[editorIndex] = "!" + cellId + "," + oldValue;
var expression = new StringOrExpression(oldValue); var expression = new StringOrExpression(oldValue);
cell.Expression = expression.Value(componentObject).ToString(); cell.Expression = expression.Value(componentObject).ToString();
componentObject.Invalidate(InvalidateType.SheetUpdated); (componentObject as Object3D).Invalidate(InvalidateType.SheetUpdated);
doOrUndoing = false; doOrUndoing = false;
}, },
() => () =>
@ -642,7 +642,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
editorList[editorIndex] = "!" + cellId + "," + newValue; editorList[editorIndex] = "!" + cellId + "," + newValue;
var expression = new StringOrExpression(newValue); var expression = new StringOrExpression(newValue);
cell.Expression = expression.Value(componentObject).ToString(); cell.Expression = expression.Value(componentObject).ToString();
componentObject.Invalidate(InvalidateType.SheetUpdated); (componentObject as Object3D).Invalidate(InvalidateType.SheetUpdated);
doOrUndoing = false; doOrUndoing = false;
})); }));
} }

View file

@ -30,7 +30,6 @@ either expressed or implied, of the FreeBSD Project.
using AngleSharp.Dom; using AngleSharp.Dom;
using AngleSharp.Html.Parser; using AngleSharp.Html.Parser;
using g3;
using MatterHackers.Agg; using MatterHackers.Agg;
using MatterHackers.Agg.Platform; using MatterHackers.Agg.Platform;
using MatterHackers.Agg.UI; using MatterHackers.Agg.UI;

View file

@ -3727,6 +3727,9 @@ Translated:Pinch
English:Pinch Slices English:Pinch Slices
Translated:Pinch Slices Translated:Pinch Slices
English:Pinch Type Value
Translated:Pinch Type Value
English:Pipe Works English:Pipe Works
Translated:Pipe Works Translated:Pipe Works

@ -1 +1 @@
Subproject commit 9545c19b13d294e47d87ab3b9ae67366827072b3 Subproject commit 35f4dca6e1635d328eaae0df6ab23bc8a300bad2