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:
parent
3a0dd553d4
commit
c4b58c9d39
13 changed files with 555 additions and 490 deletions
|
|
@ -219,19 +219,19 @@ namespace MatterHackers.MatterControl
|
|||
return new SceneOperation("EditComponent")
|
||||
{
|
||||
TitleGetter = () => "Edit Component".Localize(),
|
||||
ResultType = typeof(ComponentObject3D),
|
||||
ResultType = typeof(IComponentObject3D),
|
||||
Action = (sceneContext) =>
|
||||
{
|
||||
var scene = sceneContext.Scene;
|
||||
var sceneItem = scene.SelectedItem;
|
||||
if (sceneItem is ComponentObject3D componentObject)
|
||||
if (sceneItem is IComponentObject3D componentObject)
|
||||
{
|
||||
// Enable editing mode
|
||||
componentObject.Finalized = false;
|
||||
|
||||
// Force editor rebuild
|
||||
scene.SelectedItem = null;
|
||||
scene.SelectedItem = componentObject;
|
||||
scene.SelectedItem = componentObject as Object3D;
|
||||
|
||||
scene.Invalidate(new InvalidateArgs(null, InvalidateType.Children));
|
||||
}
|
||||
|
|
@ -242,13 +242,13 @@ namespace MatterHackers.MatterControl
|
|||
var sceneItem = scene.SelectedItem;
|
||||
return sceneItem.Parent != null
|
||||
&& sceneItem.Parent.Parent == null
|
||||
&& sceneItem is ComponentObject3D componentObject
|
||||
&& sceneItem is IComponentObject3D componentObject
|
||||
&& componentObject.Finalized
|
||||
&& !componentObject.ProOnly;
|
||||
},
|
||||
Icon = (theme) => StaticData.Instance.LoadIcon("scale_32x32.png", 16, 16).GrayToColor(theme.TextColor).SetPreMultiply(),
|
||||
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")
|
||||
{
|
||||
TitleGetter = () => "Image Converter".Localize(),
|
||||
ResultType = typeof(ComponentObject3D),
|
||||
ResultType = typeof(IComponentObject3D),
|
||||
Action = (sceneContext) =>
|
||||
{
|
||||
var scene = sceneContext.Scene;
|
||||
|
|
|
|||
|
|
@ -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++)
|
||||
{
|
||||
|
|
@ -239,7 +239,7 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
foreach (var item in itemToCheck.DescendantsAndSelf())
|
||||
{
|
||||
if (GetActiveExpression(item, checkForString, startsWith).Any()
|
||||
|| itemToCheck is ComponentObject3D component
|
||||
|| itemToCheck is IComponentObject3D component
|
||||
&& GetComponentExpressions(component, checkForString, startsWith).Any())
|
||||
{
|
||||
// three is one so return true
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
|
|||
}
|
||||
|
||||
// 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++)
|
||||
{
|
||||
|
|
@ -105,7 +105,7 @@ namespace MatterHackers.MatterControl.DesignTools.Operations
|
|||
// we don't need to rebuild our source object
|
||||
return false;
|
||||
}
|
||||
else if (item.Parent is ComponentObject3D)
|
||||
else if (item.Parent is IComponentObject3D)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
foreach (var visibleMesh in visibleMeshes)
|
||||
{
|
||||
var matrix = visibleMesh.source.WorldMatrix(object3D);
|
||||
if (visibleMesh.convexHull == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
foreach (var positon in visibleMesh.convexHull.Vertices)
|
||||
{
|
||||
var transformed = positon.Transform(matrix);
|
||||
|
|
|
|||
|
|
@ -68,6 +68,9 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
|
||||
private VertexStorage vertexStorage;
|
||||
|
||||
// the last vertex storage before the last change
|
||||
private VertexStorage beforeLastChange;
|
||||
|
||||
private ControlPointConstraint controlPointConstraint = ControlPointConstraint.Free;
|
||||
|
||||
public PathEditorWidget(VertexStorage vertexStorage,
|
||||
|
|
@ -92,6 +95,7 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
|
||||
this.unscaledRenderOffset = unscaledRenderOffset;
|
||||
this.layerScale = layerScale;
|
||||
this.undoBuffer = undoBuffer;
|
||||
|
||||
var topToBottom = this;
|
||||
|
||||
|
|
@ -105,6 +109,8 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
this.vertexChanged = vertexChanged;
|
||||
this.theme = theme;
|
||||
this.vertexStorage = vertexStorage;
|
||||
this.beforeLastChange = new VertexStorage();
|
||||
beforeLastChange.SvgDString = vertexStorage.SvgDString;
|
||||
|
||||
var toolBar = new FlowLayoutWidget()
|
||||
{
|
||||
|
|
@ -148,7 +154,7 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
var oldPosition = vertexStorage[controlPointBeingDragged].Position;
|
||||
var newPosition = new Vector2(xEditWidget.ActuallNumberEdit.Value, yEditWidget.ActuallNumberEdit.Value);
|
||||
var delta = newPosition - oldPosition;
|
||||
OffsetSelectedPoint(delta);
|
||||
OffsetSelectedPoint(delta, true);
|
||||
};
|
||||
|
||||
xEditWidget.ActuallNumberEdit.KeyDown += NumberField.InternalTextEditWidget_KeyDown;
|
||||
|
|
@ -168,7 +174,7 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
var oldPosition = vertexStorage[controlPointBeingDragged].Position;
|
||||
var newPosition = new Vector2(xEditWidget.ActuallNumberEdit.Value, yEditWidget.ActuallNumberEdit.Value);
|
||||
var delta = newPosition - oldPosition;
|
||||
OffsetSelectedPoint(delta);
|
||||
OffsetSelectedPoint(delta, true);
|
||||
};
|
||||
yEditWidget.ActuallNumberEdit.KeyDown += NumberField.InternalTextEditWidget_KeyDown;
|
||||
|
||||
|
|
@ -234,6 +240,8 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
public ETransformState TransformState { get; set; }
|
||||
private double layerScale { get; set; } = 1;
|
||||
|
||||
private UndoBuffer undoBuffer;
|
||||
|
||||
private Affine ScalingTransform => Affine.NewScaling(layerScale, layerScale);
|
||||
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 alignedButton;
|
||||
private ThemedRadioTextButton freeButton;
|
||||
private bool hasBeenStartupPositioned;
|
||||
|
||||
public void CenterPartInView()
|
||||
{
|
||||
|
|
@ -254,15 +263,29 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
var partBounds = vertexStorage.GetBounds();
|
||||
var weightedCenter = partBounds.Center;
|
||||
|
||||
var bottomPixels = 20;
|
||||
var margin = 30;
|
||||
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();
|
||||
scaleChanged?.Invoke(unscaledRenderOffset, layerScale);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
//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)
|
||||
{
|
||||
ScalingTransform.inverse_transform(ref mouseDelta);
|
||||
OffsetSelectedPoint(mouseDelta);
|
||||
OffsetSelectedPoint(mouseDelta, false);
|
||||
UpdateControlsForSelection();
|
||||
}
|
||||
}
|
||||
|
|
@ -425,15 +448,22 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
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
|
||||
|| controlPointBeingDragged >= vertexStorage.Count
|
||||
|| delta.LengthSquared == 0)
|
||||
|| controlPointBeingDragged >= vertexStorage.Count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (delta.LengthSquared > 0)
|
||||
{
|
||||
var vertexData = vertexStorage[controlPointBeingDragged];
|
||||
|
||||
if (vertexData.Hint == CommandHint.C4Point)
|
||||
|
|
@ -459,6 +489,28 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
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)
|
||||
{
|
||||
var mousePos = new Vector2(mouseEvent.X, mouseEvent.Y);
|
||||
|
|
|
|||
|
|
@ -65,9 +65,15 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
[Slider(0, 50, snapDistance: 1)]
|
||||
public IntOrExpression PinchSlices { get; set; } = 20;
|
||||
|
||||
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)]
|
||||
|
||||
[Description("Enable advanced features.")]
|
||||
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")]
|
||||
public Vector2 RotationOffset { get; set; }
|
||||
|
||||
|
|
@ -203,15 +209,11 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
}
|
||||
|
||||
var positionXy = new Vector2(position) - rotationCenter;
|
||||
var fromLine = true;
|
||||
if (fromLine)
|
||||
{
|
||||
positionXy *= horizontalOffset.GetXAtY(position.Z * 10) / (maxRadius * 10);
|
||||
//positionXy *= xAtYInterpolator.Get(position.Z * 10) / maxRadius;
|
||||
}
|
||||
else
|
||||
if (PinchTypeValue == PinchType.XAxis)
|
||||
{
|
||||
positionXy *= Easing.Quadratic.InOut(ratio);
|
||||
// only use the x value
|
||||
positionXy.Y = position.Y;
|
||||
}
|
||||
positionXy += rotationCenter;
|
||||
transformedMesh.Vertices[i] = new Vector3Float(positionXy.X, positionXy.Y, position.Z);
|
||||
|
|
|
|||
|
|
@ -27,21 +27,30 @@ of the authors and should not be interpreted as representing official policies,
|
|||
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.DataConverters3D;
|
||||
using MatterHackers.DataConverters3D.UndoCommands;
|
||||
using MatterHackers.Localizations;
|
||||
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
|
||||
{
|
||||
public interface IComponentObject3D : IObject3D
|
||||
{
|
||||
bool Finalized { get; set; }
|
||||
bool ProOnly { get; set; }
|
||||
|
||||
(string cellId, string cellData) DecodeContent(int editorIndex);
|
||||
|
||||
List<string> SurfacedEditors { get; set; }
|
||||
}
|
||||
|
||||
[HideChildrenFromTreeView]
|
||||
public class ComponentObject3D : Object3D, IRightClickMenuProvider
|
||||
public class ComponentObject3D : Object3D, IRightClickMenuProvider, IComponentObject3D
|
||||
{
|
||||
private const string ImageConverterComponentID = "4D9BD8DB-C544-4294-9C08-4195A409217A";
|
||||
|
||||
|
|
@ -169,7 +178,7 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
private void RecalculateSheet()
|
||||
{
|
||||
// if there are editors that reference cells
|
||||
for (int i=0; i<SurfacedEditors.Count; i++)
|
||||
for (int i = 0; i < SurfacedEditors.Count; i++)
|
||||
{
|
||||
var (cellId, cellData) = this.DecodeContent(i);
|
||||
if (cellData != null
|
||||
|
|
@ -213,7 +222,7 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
|
||||
public override void OnInvalidate(InvalidateArgs invalidateType)
|
||||
{
|
||||
switch(invalidateType.InvalidateType)
|
||||
switch (invalidateType.InvalidateType)
|
||||
{
|
||||
case InvalidateType.SheetUpdated:
|
||||
case InvalidateType.Properties:
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ either expressed or implied, of the FreeBSD Project.
|
|||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MatterHackers.Agg;
|
||||
using MatterHackers.Agg.VertexSource;
|
||||
using MatterHackers.DataConverters3D;
|
||||
using MatterHackers.Localizations;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (c) 2018, Lars Brubaker, John Lewin
|
||||
Copyright (c) 2023, Lars Brubaker, John Lewin
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
|
@ -27,20 +27,16 @@ of the authors and should not be interpreted as representing official policies,
|
|||
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.Image;
|
||||
using MatterHackers.Agg.UI;
|
||||
using MatterHackers.DataConverters3D;
|
||||
using MatterHackers.Localizations;
|
||||
using MatterHackers.MatterControl.CustomWidgets;
|
||||
using MatterHackers.MatterControl.DesignTools;
|
||||
using MatterHackers.MatterControl.DesignTools.Operations;
|
||||
using MatterHackers.MatterControl.Library;
|
||||
using MatterHackers.MatterControl.PartPreviewWindow.View3D;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MatterHackers.MatterControl.PartPreviewWindow
|
||||
{
|
||||
|
|
@ -56,6 +52,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
|
|||
// Suppress MeshWrapper and OperationSource nodes in tree
|
||||
bool shouldCollapseToParent = objectView.Source is ModifiedMeshObject3D || objectView.Source is OperationSourceObject3D;
|
||||
TreeNode contextNode;
|
||||
|
||||
if (shouldCollapseToParent
|
||||
&& parentTreeNode != null
|
||||
&& !keyValues.ContainsKey(objectView.Source))
|
||||
|
|
@ -70,7 +67,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
|
|||
|
||||
using (contextNode.LayoutLock())
|
||||
{
|
||||
var componentObject3D = objectView.Source as ComponentObject3D;
|
||||
var componentObject3D = objectView.Source as IComponentObject3D;
|
||||
var hideChildren = objectView.Source.GetType().GetCustomAttributes(typeof(HideChildrenFromTreeViewAttribute), true).Any();
|
||||
|
||||
if ((componentObject3D?.Finalized == false
|
||||
|
|
|
|||
|
|
@ -304,7 +304,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
|
|||
var rows = new SafeList<SettingsRow>();
|
||||
|
||||
// put in the normal editor
|
||||
if (selectedItem is ComponentObject3D componentObject
|
||||
if (selectedItem is IComponentObject3D componentObject
|
||||
&& componentObject.Finalized)
|
||||
{
|
||||
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();
|
||||
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();
|
||||
if (firtSheet != null)
|
||||
|
|
@ -633,7 +633,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
|
|||
editorList[editorIndex] = "!" + cellId + "," + oldValue;
|
||||
var expression = new StringOrExpression(oldValue);
|
||||
cell.Expression = expression.Value(componentObject).ToString();
|
||||
componentObject.Invalidate(InvalidateType.SheetUpdated);
|
||||
(componentObject as Object3D).Invalidate(InvalidateType.SheetUpdated);
|
||||
doOrUndoing = false;
|
||||
},
|
||||
() =>
|
||||
|
|
@ -642,7 +642,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
|
|||
editorList[editorIndex] = "!" + cellId + "," + newValue;
|
||||
var expression = new StringOrExpression(newValue);
|
||||
cell.Expression = expression.Value(componentObject).ToString();
|
||||
componentObject.Invalidate(InvalidateType.SheetUpdated);
|
||||
(componentObject as Object3D).Invalidate(InvalidateType.SheetUpdated);
|
||||
doOrUndoing = false;
|
||||
}));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ either expressed or implied, of the FreeBSD Project.
|
|||
|
||||
using AngleSharp.Dom;
|
||||
using AngleSharp.Html.Parser;
|
||||
using g3;
|
||||
using MatterHackers.Agg;
|
||||
using MatterHackers.Agg.Platform;
|
||||
using MatterHackers.Agg.UI;
|
||||
|
|
|
|||
|
|
@ -3727,6 +3727,9 @@ Translated:Pinch
|
|||
English:Pinch Slices
|
||||
Translated:Pinch Slices
|
||||
|
||||
English:Pinch Type Value
|
||||
Translated:Pinch Type Value
|
||||
|
||||
English:Pipe Works
|
||||
Translated:Pipe Works
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 9545c19b13d294e47d87ab3b9ae67366827072b3
|
||||
Subproject commit 35f4dca6e1635d328eaae0df6ab23bc8a300bad2
|
||||
Loading…
Add table
Add a link
Reference in a new issue