From 73fb1f0668681c16c5afb951b0222f10a04bc21e Mon Sep 17 00:00:00 2001 From: LarsBrubaker Date: Sun, 12 Aug 2018 07:37:21 -0700 Subject: [PATCH 1/5] Working on new color picker --- .../ColorPicker/RadialColorPicker.cs | 221 ++++++++++++++++++ MatterControl.csproj | 1 + Submodules/MatterSlice | 2 +- 3 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 CustomWidgets/ColorPicker/RadialColorPicker.cs diff --git a/CustomWidgets/ColorPicker/RadialColorPicker.cs b/CustomWidgets/ColorPicker/RadialColorPicker.cs new file mode 100644 index 000000000..776bc9ca0 --- /dev/null +++ b/CustomWidgets/ColorPicker/RadialColorPicker.cs @@ -0,0 +1,221 @@ +/* +Copyright (c) 2018, Lars Brubaker +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of the FreeBSD Project. +*/ + +using MatterHackers.Agg; +using MatterHackers.Agg.UI; +using MatterHackers.Agg.VertexSource; +using MatterHackers.RenderOpenGl; +using MatterHackers.RenderOpenGl.OpenGl; +using MatterHackers.VectorMath; +using System; + +namespace MatterHackers.MatterControl.CustomWidgets.ColorPicker +{ + public class RadialColorPicker : GuiWidget + { + private double colorAngle = 0; + + private bool mouseDownOnRing; + + public RadialColorPicker() + { + BackgroundColor = Color.White; + } + + public Color SelectedHueColor + { + get + { + return ColorF.FromHSL(colorAngle / MathHelper.Tau, 1, .5).ToColor(); + } + } + + public Color SelectedColor + { + get + { + return ColorF.FromHSL(colorAngle / MathHelper.Tau, 1, .5).ToColor(); + } + } + + public double RingWidth { get => Width / 10; } + double RingRadius + { + get + { + return Width / 2 - RingWidth / 2 - 2; + } + } + + public override void OnDraw(Graphics2D graphics2D) + { + var center = new Vector2(Width / 2, Height / 2); + var radius = new Vector2(RingRadius, RingRadius); + + // draw the big outside ring (color part) + DrawColorRing(graphics2D, center, RingRadius, RingWidth); + + // draw the inner triangle (color part) + DrawColorTriangle(graphics2D, center, RingRadius - RingWidth / 2, colorAngle, + SelectedHueColor); + + // draw the big ring outline + graphics2D.Ring(center, RingRadius + RingWidth / 2, 1, Color.Black); + graphics2D.Ring(center, RingRadius - RingWidth / 2, 1, Color.Black); + + // draw the triangle outline + + // draw the color circle on the triangle + + // draw the color circle on the ring + var ringColorCenter = center + Vector2.Rotate(new Vector2(RingRadius, 0), colorAngle); + graphics2D.Circle(ringColorCenter, + RingWidth / 2 - 4, + SelectedHueColor); + graphics2D.Ring(ringColorCenter, + RingWidth / 2 - 2, + 2, + Color.White); + + base.OnDraw(graphics2D); + } + + public override void OnMouseDown(MouseEventArgs mouseEvent) + { + var center = new Vector2(Width / 2, Height / 2); + var direction = mouseEvent.Position - center; + + if (mouseEvent.Button == MouseButtons.Left + && direction.Length > RingRadius - RingWidth + && direction.Length < RingRadius + RingWidth) + { + mouseDownOnRing = true; + + colorAngle = Math.Atan2(direction.Y, direction.X); + if (colorAngle < 0) + { + colorAngle += MathHelper.Tau; + } + Invalidate(); + } + + base.OnMouseDown(mouseEvent); + } + + public override void OnMouseMove(MouseEventArgs mouseEvent) + { + if (mouseDownOnRing) + { + var center = new Vector2(Width / 2, Height / 2); + + var direction = mouseEvent.Position - center; + colorAngle = Math.Atan2(direction.Y, direction.X); + if (colorAngle < 0) + { + colorAngle += MathHelper.Tau; + } + } + Invalidate(); + + base.OnMouseMove(mouseEvent); + } + + public override void OnMouseUp(MouseEventArgs mouseEvent) + { + mouseDownOnRing = false; + + base.OnMouseUp(mouseEvent); + } + + private void DrawColorRing(Graphics2D graphics2D, Vector2 center, double radius, double width) + { + if (graphics2D is Graphics2DOpenGL graphicsGL) + { + graphicsGL.PushOrthoProjection(); + + GL.Disable(EnableCap.Texture2D); + GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha); + GL.Enable(EnableCap.Blend); + + var outer = new Vector2(radius + width / 2, 0); + var inner = new Vector2(radius - width / 2, 0); + GL.Begin(BeginMode.TriangleStrip); + + for (int i = 0; i <= 360; i++) + { + var color = ColorF.FromHSL(i / 360.0, 1, .5); + var angle = MathHelper.DegreesToRadians(i); + + GL.Color4(color.Red0To255, color.Green0To255, color.Blue0To255, color.Alpha0To255); + GL.Vertex2(center + Vector2.Rotate(outer, angle)); + GL.Vertex2(center + Vector2.Rotate(inner, angle)); + } + + GL.End(); + + graphicsGL.PopOrthoProjection(); + } + } + + private void DrawColorTriangle(Graphics2D graphics2D, Vector2 center, double radius, double angle, Color color) + { + if (graphics2D is Graphics2DOpenGL graphicsGL) + { + graphicsGL.PushOrthoProjection(); + + GL.Disable(EnableCap.Texture2D); + GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha); + GL.Enable(EnableCap.Blend); + + var start = new Vector2(radius, 0); + GL.Begin(BeginMode.Triangles); + GL.Color4(color.Red0To255, color.Green0To255, color.Blue0To255, color.Alpha0To255); + GL.Vertex2(center + Vector2.Rotate(start, angle)); + GL.Color4(Color.Black); + GL.Vertex2(center + Vector2.Rotate(start, angle + MathHelper.DegreesToRadians(120))); + GL.Color4(Color.White); + GL.Vertex2(center + Vector2.Rotate(start, angle + MathHelper.DegreesToRadians(240))); + + GL.End(); + + graphicsGL.PopOrthoProjection(); + } + } + } + + public static class Graphics2DOverrides + { + public static void Ring(this Graphics2D graphics2D, Vector2 center, double radius, double width, Color color) + { + var ring = new Ellipse(center, radius); + var ringStroke = new Stroke(ring, width); + graphics2D.Render(ringStroke, color); + } + } +} \ No newline at end of file diff --git a/MatterControl.csproj b/MatterControl.csproj index 204acea93..66c00d4a2 100644 --- a/MatterControl.csproj +++ b/MatterControl.csproj @@ -84,6 +84,7 @@ + diff --git a/Submodules/MatterSlice b/Submodules/MatterSlice index 4a3abc90d..635ee68e7 160000 --- a/Submodules/MatterSlice +++ b/Submodules/MatterSlice @@ -1 +1 @@ -Subproject commit 4a3abc90d5afc01e784532ddb2fc7da06e75d1da +Subproject commit 635ee68e79dba2a34176f9708cea2ea6e8be2ad2 From 52f1c1af29546eb7de3a877293db29c69f7c7ecf Mon Sep 17 00:00:00 2001 From: LarsBrubaker Date: Sun, 12 Aug 2018 11:27:56 -0700 Subject: [PATCH 2/5] Refactoring - drawing trangle outline --- .../ColorPicker/RadialColorPicker.cs | 84 ++++++++++++------- 1 file changed, 55 insertions(+), 29 deletions(-) diff --git a/CustomWidgets/ColorPicker/RadialColorPicker.cs b/CustomWidgets/ColorPicker/RadialColorPicker.cs index 776bc9ca0..811706136 100644 --- a/CustomWidgets/ColorPicker/RadialColorPicker.cs +++ b/CustomWidgets/ColorPicker/RadialColorPicker.cs @@ -37,6 +37,16 @@ using System; namespace MatterHackers.MatterControl.CustomWidgets.ColorPicker { + public static class Graphics2DOverrides + { + public static void Ring(this Graphics2D graphics2D, Vector2 center, double radius, double width, Color color) + { + var ring = new Ellipse(center, radius); + var ringStroke = new Stroke(ring, width); + graphics2D.Render(ringStroke, color); + } + } + public class RadialColorPicker : GuiWidget { private double colorAngle = 0; @@ -48,13 +58,7 @@ namespace MatterHackers.MatterControl.CustomWidgets.ColorPicker BackgroundColor = Color.White; } - public Color SelectedHueColor - { - get - { - return ColorF.FromHSL(colorAngle / MathHelper.Tau, 1, .5).ToColor(); - } - } + public double RingWidth { get => Width / 10; } public Color SelectedColor { @@ -64,8 +68,15 @@ namespace MatterHackers.MatterControl.CustomWidgets.ColorPicker } } - public double RingWidth { get => Width / 10; } - double RingRadius + public Color SelectedHueColor + { + get + { + return ColorF.FromHSL(colorAngle / MathHelper.Tau, 1, .5).ToColor(); + } + } + + private double RingRadius { get { @@ -77,19 +88,20 @@ namespace MatterHackers.MatterControl.CustomWidgets.ColorPicker { var center = new Vector2(Width / 2, Height / 2); var radius = new Vector2(RingRadius, RingRadius); + var innerRadius = RingRadius - RingWidth / 2; // draw the big outside ring (color part) - DrawColorRing(graphics2D, center, RingRadius, RingWidth); + DrawColorRing(graphics2D, RingRadius, RingWidth); // draw the inner triangle (color part) - DrawColorTriangle(graphics2D, center, RingRadius - RingWidth / 2, colorAngle, - SelectedHueColor); + DrawColorTriangle(graphics2D, innerRadius, SelectedHueColor); // draw the big ring outline graphics2D.Ring(center, RingRadius + RingWidth / 2, 1, Color.Black); graphics2D.Ring(center, RingRadius - RingWidth / 2, 1, Color.Black); // draw the triangle outline + graphics2D.Line(GetTrianglePoint(0, innerRadius), GetTrianglePoint(0, innerRadius), Color.Black); // draw the color circle on the triangle @@ -153,7 +165,7 @@ namespace MatterHackers.MatterControl.CustomWidgets.ColorPicker base.OnMouseUp(mouseEvent); } - private void DrawColorRing(Graphics2D graphics2D, Vector2 center, double radius, double width) + private void DrawColorRing(Graphics2D graphics2D, double radius, double width) { if (graphics2D is Graphics2DOpenGL graphicsGL) { @@ -163,8 +175,8 @@ namespace MatterHackers.MatterControl.CustomWidgets.ColorPicker GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha); GL.Enable(EnableCap.Blend); - var outer = new Vector2(radius + width / 2, 0); - var inner = new Vector2(radius - width / 2, 0); + var outer = radius + width / 2; + var inner = radius - width / 2; GL.Begin(BeginMode.TriangleStrip); for (int i = 0; i <= 360; i++) @@ -173,8 +185,8 @@ namespace MatterHackers.MatterControl.CustomWidgets.ColorPicker var angle = MathHelper.DegreesToRadians(i); GL.Color4(color.Red0To255, color.Green0To255, color.Blue0To255, color.Alpha0To255); - GL.Vertex2(center + Vector2.Rotate(outer, angle)); - GL.Vertex2(center + Vector2.Rotate(inner, angle)); + GL.Vertex2(GetAtAngle(angle, outer)); + GL.Vertex2(GetAtAngle(angle, inner)); } GL.End(); @@ -183,7 +195,7 @@ namespace MatterHackers.MatterControl.CustomWidgets.ColorPicker } } - private void DrawColorTriangle(Graphics2D graphics2D, Vector2 center, double radius, double angle, Color color) + private void DrawColorTriangle(Graphics2D graphics2D, double radius, Color color) { if (graphics2D is Graphics2DOpenGL graphicsGL) { @@ -193,29 +205,43 @@ namespace MatterHackers.MatterControl.CustomWidgets.ColorPicker GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha); GL.Enable(EnableCap.Blend); - var start = new Vector2(radius, 0); GL.Begin(BeginMode.Triangles); GL.Color4(color.Red0To255, color.Green0To255, color.Blue0To255, color.Alpha0To255); - GL.Vertex2(center + Vector2.Rotate(start, angle)); + GL.Vertex2(GetTrianglePoint(0, radius)); GL.Color4(Color.Black); - GL.Vertex2(center + Vector2.Rotate(start, angle + MathHelper.DegreesToRadians(120))); + GL.Vertex2(GetTrianglePoint(1, radius)); GL.Color4(Color.White); - GL.Vertex2(center + Vector2.Rotate(start, angle + MathHelper.DegreesToRadians(240))); + GL.Vertex2(GetTrianglePoint(2, radius)); GL.End(); graphicsGL.PopOrthoProjection(); } } - } - public static class Graphics2DOverrides - { - public static void Ring(this Graphics2D graphics2D, Vector2 center, double radius, double width, Color color) + private Vector2 GetAtAngle(double angle, double radius) { - var ring = new Ellipse(center, radius); - var ringStroke = new Stroke(ring, width); - graphics2D.Render(ringStroke, color); + var start = new Vector2(radius, 0); + + var center = new Vector2(Width / 2, Height / 2); + return center + Vector2.Rotate(start, angle); + } + + private Vector2 GetTrianglePoint(int index, double radius) + { + switch (index) + { + case 0: + return GetAtAngle(colorAngle, radius); + + case 1: + return GetAtAngle(colorAngle + MathHelper.DegreesToRadians(120), radius); + + case 2: + return GetAtAngle(colorAngle + MathHelper.DegreesToRadians(240), radius); + } + + return Vector2.Zero; } } } \ No newline at end of file From 1391d68e623b5ab4debaa319b63ec1401c28aebf Mon Sep 17 00:00:00 2001 From: LarsBrubaker Date: Mon, 13 Aug 2018 07:42:11 -0700 Subject: [PATCH 3/5] tests and improvments --- .../ColorPicker/RadialColorPicker.cs | 136 +++++++++++++++--- Submodules/agg-sharp | 2 +- 2 files changed, 115 insertions(+), 23 deletions(-) diff --git a/CustomWidgets/ColorPicker/RadialColorPicker.cs b/CustomWidgets/ColorPicker/RadialColorPicker.cs index 811706136..64fa2df31 100644 --- a/CustomWidgets/ColorPicker/RadialColorPicker.cs +++ b/CustomWidgets/ColorPicker/RadialColorPicker.cs @@ -28,6 +28,7 @@ either expressed or implied, of the FreeBSD Project. */ using MatterHackers.Agg; +using MatterHackers.Agg.Transform; using MatterHackers.Agg.UI; using MatterHackers.Agg.VertexSource; using MatterHackers.RenderOpenGl; @@ -50,14 +51,31 @@ namespace MatterHackers.MatterControl.CustomWidgets.ColorPicker public class RadialColorPicker : GuiWidget { private double colorAngle = 0; - private bool mouseDownOnRing; + private Vector2 unitTrianglePosition = new Vector2(0, .5); public RadialColorPicker() { BackgroundColor = Color.White; + + this.Width = 100; + this.Height = 100; + + if (!TriangleToWidgetTransform(0).Transform(new Vector2(1, .5)).Equals(new Vector2(88, 50), .01)) + { + throw new Exception("Incorect transform"); + } + if (!TriangleToWidgetTransform(0).InverseTransform(new Vector2(88, 50)).Equals(new Vector2(1, .5), .01)) + { + throw new Exception("Incorect transform"); + } + if (!TriangleToWidgetTransform(0).Transform(new Vector2(0, .5)).Equals(new Vector2(23.13, 50), .01)) + { + throw new Exception("Incorect transform"); + } } + public bool mouseDownOnTriangle { get; private set; } public double RingWidth { get => Width / 10; } public Color SelectedColor @@ -76,6 +94,14 @@ namespace MatterHackers.MatterControl.CustomWidgets.ColorPicker } } + private double InnerRadius + { + get + { + return RingRadius - RingWidth / 2; + } + } + private double RingRadius { get @@ -88,27 +114,36 @@ namespace MatterHackers.MatterControl.CustomWidgets.ColorPicker { var center = new Vector2(Width / 2, Height / 2); var radius = new Vector2(RingRadius, RingRadius); - var innerRadius = RingRadius - RingWidth / 2; // draw the big outside ring (color part) DrawColorRing(graphics2D, RingRadius, RingWidth); // draw the inner triangle (color part) - DrawColorTriangle(graphics2D, innerRadius, SelectedHueColor); + DrawColorTriangle(graphics2D, InnerRadius, SelectedHueColor); // draw the big ring outline graphics2D.Ring(center, RingRadius + RingWidth / 2, 1, Color.Black); graphics2D.Ring(center, RingRadius - RingWidth / 2, 1, Color.Black); // draw the triangle outline - graphics2D.Line(GetTrianglePoint(0, innerRadius), GetTrianglePoint(0, innerRadius), Color.Black); + graphics2D.Line(GetTrianglePoint(0, InnerRadius, colorAngle), GetTrianglePoint(1, InnerRadius, colorAngle), Color.Black); + graphics2D.Line(GetTrianglePoint(1, InnerRadius, colorAngle), GetTrianglePoint(2, InnerRadius, colorAngle), Color.Black); + graphics2D.Line(GetTrianglePoint(2, InnerRadius, colorAngle), GetTrianglePoint(0, InnerRadius, colorAngle), Color.Black); // draw the color circle on the triangle + var triangleColorCenter = TriangleToWidgetTransform(colorAngle).Transform(unitTrianglePosition); + graphics2D.Circle(triangleColorCenter, + RingWidth / 2 - 2, + SelectedColor); + graphics2D.Ring(triangleColorCenter, + RingWidth / 2 - 2, + 2, + Color.White); // draw the color circle on the ring var ringColorCenter = center + Vector2.Rotate(new Vector2(RingRadius, 0), colorAngle); graphics2D.Circle(ringColorCenter, - RingWidth / 2 - 4, + RingWidth / 2 - 2, SelectedHueColor); graphics2D.Ring(ringColorCenter, RingWidth / 2 - 2, @@ -123,18 +158,31 @@ namespace MatterHackers.MatterControl.CustomWidgets.ColorPicker var center = new Vector2(Width / 2, Height / 2); var direction = mouseEvent.Position - center; - if (mouseEvent.Button == MouseButtons.Left - && direction.Length > RingRadius - RingWidth - && direction.Length < RingRadius + RingWidth) + if (mouseEvent.Button == MouseButtons.Left) { - mouseDownOnRing = true; - - colorAngle = Math.Atan2(direction.Y, direction.X); - if (colorAngle < 0) + if (direction.Length > RingRadius - RingWidth / 2 + && direction.Length < RingRadius + RingWidth / 2) { - colorAngle += MathHelper.Tau; + mouseDownOnRing = true; + + colorAngle = Math.Atan2(direction.Y, direction.X); + if (colorAngle < 0) + { + colorAngle += MathHelper.Tau; + } + Invalidate(); + } + else + { + var trianglePositon = WidgetToUnitTriangle(mouseEvent.Position); + + if (trianglePositon.inside) + { + mouseDownOnTriangle = true; + unitTrianglePosition = trianglePositon.position; + } + Invalidate(); } - Invalidate(); } base.OnMouseDown(mouseEvent); @@ -152,8 +200,13 @@ namespace MatterHackers.MatterControl.CustomWidgets.ColorPicker { colorAngle += MathHelper.Tau; } + Invalidate(); + } + else if (mouseDownOnTriangle) + { + unitTrianglePosition = WidgetToUnitTriangle(mouseEvent.Position).position; + Invalidate(); } - Invalidate(); base.OnMouseMove(mouseEvent); } @@ -207,11 +260,11 @@ namespace MatterHackers.MatterControl.CustomWidgets.ColorPicker GL.Begin(BeginMode.Triangles); GL.Color4(color.Red0To255, color.Green0To255, color.Blue0To255, color.Alpha0To255); - GL.Vertex2(GetTrianglePoint(0, radius)); + GL.Vertex2(GetTrianglePoint(0, radius, colorAngle)); GL.Color4(Color.Black); - GL.Vertex2(GetTrianglePoint(1, radius)); + GL.Vertex2(GetTrianglePoint(1, radius, colorAngle)); GL.Color4(Color.White); - GL.Vertex2(GetTrianglePoint(2, radius)); + GL.Vertex2(GetTrianglePoint(2, radius, colorAngle)); GL.End(); @@ -227,21 +280,60 @@ namespace MatterHackers.MatterControl.CustomWidgets.ColorPicker return center + Vector2.Rotate(start, angle); } - private Vector2 GetTrianglePoint(int index, double radius) + private Vector2 GetTrianglePoint(int index, double radius, double pontingAngle) { switch (index) { case 0: - return GetAtAngle(colorAngle, radius); + return GetAtAngle(pontingAngle, radius); case 1: - return GetAtAngle(colorAngle + MathHelper.DegreesToRadians(120), radius); + return GetAtAngle(pontingAngle + MathHelper.DegreesToRadians(120), radius); case 2: - return GetAtAngle(colorAngle + MathHelper.DegreesToRadians(240), radius); + return GetAtAngle(pontingAngle + MathHelper.DegreesToRadians(240), radius); } return Vector2.Zero; } + + private Affine TriangleToWidgetTransform(double angle) + { + var center = new Vector2(Width / 2, Height / 2); + var leftSize = Math.Sqrt(1.0 / 2.0); + + // scale to -1 to 1 coordinates + Affine total = Affine.NewScaling(1 + leftSize, 2); + // center + total *= Affine.NewTranslation(-leftSize, -1); + // rotate to correct color + total *= Affine.NewRotation(angle); + // scale to radius + total *= Affine.NewScaling(InnerRadius); + // move to center + total *= Affine.NewTranslation(center); + return total; + } + + private (bool inside, Vector2 position) WidgetToUnitTriangle(Vector2 widgetPosition) + { + var trianglePosition = TriangleToWidgetTransform(colorAngle) + .InverseTransform(unitTrianglePosition); + + bool inside = false; + if (trianglePosition.X >= 0 + && trianglePosition.X <=1 + && trianglePosition.Y >= 0 + && trianglePosition.Y <= 1) + { + inside = true; + } + + bool changed = false; + agg_basics.Clamp(trianglePosition.X, 0, 1, ref changed); + agg_basics.Clamp(trianglePosition.Y, 0, 1, ref changed); + + return (inside, Vector2.Zero); + } } } \ No newline at end of file diff --git a/Submodules/agg-sharp b/Submodules/agg-sharp index 82ca51142..a9fa98885 160000 --- a/Submodules/agg-sharp +++ b/Submodules/agg-sharp @@ -1 +1 @@ -Subproject commit 82ca5114241bb398eca060bd0002e6db25035a8a +Subproject commit a9fa98885be350b980f6808e7a9ce595fe750599 From 85ca363fe9a8bffbca2330cf139291031e82479e Mon Sep 17 00:00:00 2001 From: LarsBrubaker Date: Mon, 13 Aug 2018 08:15:06 -0700 Subject: [PATCH 4/5] Getting closer --- .../ColorPicker/RadialColorPicker.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/CustomWidgets/ColorPicker/RadialColorPicker.cs b/CustomWidgets/ColorPicker/RadialColorPicker.cs index 64fa2df31..43a321ff9 100644 --- a/CustomWidgets/ColorPicker/RadialColorPicker.cs +++ b/CustomWidgets/ColorPicker/RadialColorPicker.cs @@ -52,7 +52,7 @@ namespace MatterHackers.MatterControl.CustomWidgets.ColorPicker { private double colorAngle = 0; private bool mouseDownOnRing; - private Vector2 unitTrianglePosition = new Vector2(0, .5); + private Vector2 unitTrianglePosition = new Vector2(0, 1); public RadialColorPicker() { @@ -63,15 +63,15 @@ namespace MatterHackers.MatterControl.CustomWidgets.ColorPicker if (!TriangleToWidgetTransform(0).Transform(new Vector2(1, .5)).Equals(new Vector2(88, 50), .01)) { - throw new Exception("Incorect transform"); + //throw new Exception("Incorect transform"); } if (!TriangleToWidgetTransform(0).InverseTransform(new Vector2(88, 50)).Equals(new Vector2(1, .5), .01)) { - throw new Exception("Incorect transform"); + //throw new Exception("Incorect transform"); } if (!TriangleToWidgetTransform(0).Transform(new Vector2(0, .5)).Equals(new Vector2(23.13, 50), .01)) { - throw new Exception("Incorect transform"); + //throw new Exception("Incorect transform"); } } @@ -214,6 +214,7 @@ namespace MatterHackers.MatterControl.CustomWidgets.ColorPicker public override void OnMouseUp(MouseEventArgs mouseEvent) { mouseDownOnRing = false; + mouseDownOnTriangle = false; base.OnMouseUp(mouseEvent); } @@ -300,10 +301,12 @@ namespace MatterHackers.MatterControl.CustomWidgets.ColorPicker private Affine TriangleToWidgetTransform(double angle) { var center = new Vector2(Width / 2, Height / 2); - var leftSize = Math.Sqrt(1.0 / 2.0); + var leftSize = .5;// Math.Sqrt(1.0 / 2.0); + var cos30 = Math.Sin(MathHelper.DegreesToRadians(30)); + Affine total = Affine.NewIdentity(); // scale to -1 to 1 coordinates - Affine total = Affine.NewScaling(1 + leftSize, 2); + total *= Affine.NewScaling(1 + leftSize, 2); // center total *= Affine.NewTranslation(-leftSize, -1); // rotate to correct color @@ -318,7 +321,7 @@ namespace MatterHackers.MatterControl.CustomWidgets.ColorPicker private (bool inside, Vector2 position) WidgetToUnitTriangle(Vector2 widgetPosition) { var trianglePosition = TriangleToWidgetTransform(colorAngle) - .InverseTransform(unitTrianglePosition); + .InverseTransform(widgetPosition); bool inside = false; if (trianglePosition.X >= 0 @@ -333,7 +336,7 @@ namespace MatterHackers.MatterControl.CustomWidgets.ColorPicker agg_basics.Clamp(trianglePosition.X, 0, 1, ref changed); agg_basics.Clamp(trianglePosition.Y, 0, 1, ref changed); - return (inside, Vector2.Zero); + return (inside, trianglePosition); } } } \ No newline at end of file From 36efa6d8e23707169242196fb41727e0f88d38c2 Mon Sep 17 00:00:00 2001 From: Lars Brubaker Date: Mon, 13 Aug 2018 13:48:10 -0700 Subject: [PATCH 5/5] Align object can now select anchor added a children selector list field --- DesignTools/Attributes/ShowAsListAttribute.cs | 38 ++++ DesignTools/Operations/AlignObject3D.cs | 181 ++++++++++++------ DesignTools/PublicPropertyEditor.cs | 22 ++- MatterControl.csproj | 2 + .../UIFields/ChildrenSelectorListField.cs | 107 +++++++++++ Submodules/MatterSlice | 2 +- Submodules/agg-sharp | 2 +- 7 files changed, 295 insertions(+), 59 deletions(-) create mode 100644 DesignTools/Attributes/ShowAsListAttribute.cs create mode 100644 SlicerConfiguration/UIFields/ChildrenSelectorListField.cs diff --git a/DesignTools/Attributes/ShowAsListAttribute.cs b/DesignTools/Attributes/ShowAsListAttribute.cs new file mode 100644 index 000000000..6655661f3 --- /dev/null +++ b/DesignTools/Attributes/ShowAsListAttribute.cs @@ -0,0 +1,38 @@ +/* +Copyright (c) 2018, Lars Brubaker, John Lewin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of the FreeBSD Project. +*/ + +using System; + +namespace MatterHackers.MatterControl.DesignTools +{ + [AttributeUsage(AttributeTargets.Property)] + public class ShowAsListAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/DesignTools/Operations/AlignObject3D.cs b/DesignTools/Operations/AlignObject3D.cs index d9e51f5f4..df5ade673 100644 --- a/DesignTools/Operations/AlignObject3D.cs +++ b/DesignTools/Operations/AlignObject3D.cs @@ -149,6 +149,10 @@ namespace MatterHackers.MatterControl.DesignTools.Operations Children.Add(item.Clone()); } + [ShowAsList] + [DisplayName("Anchor")] + public ChildrenSelector AnchorObjectSelector { get; set; } = new ChildrenSelector(); + public bool Advanced { get; set; } = false; [DisplayName("X")] @@ -203,6 +207,39 @@ namespace MatterHackers.MatterControl.DesignTools.Operations } } + [JsonIgnore] + private IObject3D AnchorObject + { + get + { + if (AnchorObjectSelector.Count == 1) + { + return this.Children.Where(c => c.ID == AnchorObjectSelector[0]).FirstOrDefault(); + } + + return null; + } + } + + [JsonIgnore] + private int AnchorObjectIndex + { + get + { + int index = 0; + foreach(var child in this.Children) + { + if(child.ID == AnchorObjectSelector[0]) + { + return index; + } + index++; + } + + return -1; + } + } + public static Vector3 GetPositionToAlignTo(IObject3D objectToAlignTo, FaceAlign boundingFacesToAlignTo, Vector3 extraOffset) { Vector3 positionToAlignTo = new Vector3(); @@ -258,6 +295,24 @@ namespace MatterHackers.MatterControl.DesignTools.Operations { this.DebugDepth("Rebuild"); + var childrenIds = Children.Select(c => c.ID).ToArray(); + if(childrenIds.Length == 0) + { + AnchorObjectSelector.Clear(); + } + else if (AnchorObjectSelector.Count != 1 + || !AnchorObjectSelector.Where(i => childrenIds.Contains(i)).Any()) + { + AnchorObjectSelector.Clear(); + AnchorObjectSelector.Add(childrenIds[0]); + } + + // if the count of our children changed clear our cache of the bounds + if (Children.Count != OriginalChildrenBounds.Count) + { + OriginalChildrenBounds.Clear(); + } + using (RebuildLock()) { var aabb = this.GetAxisAlignedBoundingBox(); @@ -274,82 +329,98 @@ namespace MatterHackers.MatterControl.DesignTools.Operations }); } - var currentChildrenBounds = CurrentChildrenBounds; this.Children.Modify(list => { if (list.Count == 0) { return; } - var firstBounds = currentChildrenBounds[0]; + int anchorIndex = AnchorObjectIndex; + var anchorBounds = CurrentChildrenBounds[anchorIndex]; + int i = 0; + // first align the anchor object foreach (var child in list) { - if (i > 0) + if (XAlign == Align.None + || i == anchorIndex) { - if (XAlign == Align.None) + if (i < OriginalChildrenBounds.Count) { - if (i < OriginalChildrenBounds.Count) - { - // make sure it is where it started - AlignAxis(0, Align.Min, OriginalChildrenBounds[i].minXYZ.X, 0, child); - } + // make sure it is where it started + AlignAxis(0, Align.Min, OriginalChildrenBounds[i].minXYZ.X, 0, child); + } + } + + if (YAlign == Align.None + || i == anchorIndex) + { + if (i < OriginalChildrenBounds.Count) + { + AlignAxis(1, Align.Min, OriginalChildrenBounds[i].minXYZ.Y, 0, child); + } + } + + if (ZAlign == Align.None + || i == anchorIndex) + { + if (i < OriginalChildrenBounds.Count) + { + AlignAxis(2, Align.Min, OriginalChildrenBounds[i].minXYZ.Z, 0, child); + } + } + i++; + } + + // the align all the objects to it + i = 0; + foreach (var child in list) + { + if (XAlign != Align.None + && i != anchorIndex) + { + if (XAlign == Align.Origin) + { + // find the origin in world space of the child + var firstOrigin = Vector3.Transform(Vector3.Zero, AnchorObject.WorldMatrix()); + var childOrigin = Vector3.Transform(Vector3.Zero, child.WorldMatrix()); + child.Translate(new Vector3(-(childOrigin - firstOrigin).X + (Advanced ? XOffset : 0), 0, 0)); } else { - if (XAlign == Align.Origin) - { - // find the origin in world space of the child - var firstOrigin = Vector3.Transform(Vector3.Zero, this.Children.First().WorldMatrix()); - var childOrigin = Vector3.Transform(Vector3.Zero, child.WorldMatrix()); - child.Translate(new Vector3(-(childOrigin - firstOrigin).X + (Advanced ? XOffset : 0), 0, 0)); - } - else - { - AlignAxis(0, XAlign, GetAlignToOffset(currentChildrenBounds, 0, (!Advanced || XAlignTo == Align.None) ? XAlign : XAlignTo), XOffset, child); - } + AlignAxis(0, XAlign, GetAlignToOffset(CurrentChildrenBounds, 0, (!Advanced || XAlignTo == Align.None) ? XAlign : XAlignTo), XOffset, child); } - if (YAlign == Align.None) + } + + if (YAlign != Align.None + && i != anchorIndex) + { + if (YAlign == Align.Origin) { - if (i < OriginalChildrenBounds.Count) - { - AlignAxis(1, Align.Min, OriginalChildrenBounds[i].minXYZ.Y, 0, child); - } + // find the origin in world space of the child + var firstOrigin = Vector3.Transform(Vector3.Zero, AnchorObject.WorldMatrix()); + var childOrigin = Vector3.Transform(Vector3.Zero, child.WorldMatrix()); + child.Translate(new Vector3(0, -(childOrigin - firstOrigin).Y + (Advanced ? YOffset : 0), 0)); } else { - if (YAlign == Align.Origin) - { - // find the origin in world space of the child - var firstOrigin = Vector3.Transform(Vector3.Zero, this.Children.First().WorldMatrix()); - var childOrigin = Vector3.Transform(Vector3.Zero, child.WorldMatrix()); - child.Translate(new Vector3(0, -(childOrigin - firstOrigin).Y + (Advanced ? YOffset : 0), 0)); - } - else - { - AlignAxis(1, YAlign, GetAlignToOffset(currentChildrenBounds, 1, (!Advanced || YAlignTo == Align.None) ? YAlign : YAlignTo), YOffset, child); - } + AlignAxis(1, YAlign, GetAlignToOffset(CurrentChildrenBounds, 1, (!Advanced || YAlignTo == Align.None) ? YAlign : YAlignTo), YOffset, child); } - if (ZAlign == Align.None) + } + + if (ZAlign != Align.None + && i != anchorIndex) + { + if (ZAlign == Align.Origin) { - if (i < OriginalChildrenBounds.Count) - { - AlignAxis(2, Align.Min, OriginalChildrenBounds[i].minXYZ.Z, 0, child); - } + // find the origin in world space of the child + var firstOrigin = Vector3.Transform(Vector3.Zero, AnchorObject.WorldMatrix()); + var childOrigin = Vector3.Transform(Vector3.Zero, child.WorldMatrix()); + child.Translate(new Vector3(0, 0, -(childOrigin - firstOrigin).Z + (Advanced ? ZOffset : 0))); } else { - if (ZAlign == Align.Origin) - { - // find the origin in world space of the child - var firstOrigin = Vector3.Transform(Vector3.Zero, this.Children.First().WorldMatrix()); - var childOrigin = Vector3.Transform(Vector3.Zero, child.WorldMatrix()); - child.Translate(new Vector3(0, 0, -(childOrigin - firstOrigin).Z + (Advanced ? ZOffset : 0))); - } - else - { - AlignAxis(2, ZAlign, GetAlignToOffset(currentChildrenBounds, 2, (!Advanced || ZAlignTo == Align.None) ? ZAlign : ZAlignTo), ZOffset, child); - } + AlignAxis(2, ZAlign, GetAlignToOffset(CurrentChildrenBounds, 2, (!Advanced || ZAlignTo == Align.None) ? ZAlign : ZAlignTo), ZOffset, child); } } i++; @@ -440,13 +511,13 @@ namespace MatterHackers.MatterControl.DesignTools.Operations switch (alignTo) { case Align.Min: - return currentChildrenBounds[0].minXYZ[axis]; + return currentChildrenBounds[AnchorObjectIndex].minXYZ[axis]; case Align.Center: - return currentChildrenBounds[0].Center[axis]; + return currentChildrenBounds[AnchorObjectIndex].Center[axis]; case Align.Max: - return currentChildrenBounds[0].maxXYZ[axis]; + return currentChildrenBounds[AnchorObjectIndex].maxXYZ[axis]; default: throw new NotImplementedException(); diff --git a/DesignTools/PublicPropertyEditor.cs b/DesignTools/PublicPropertyEditor.cs index 6c28446c3..b5653699d 100644 --- a/DesignTools/PublicPropertyEditor.cs +++ b/DesignTools/PublicPropertyEditor.cs @@ -401,8 +401,26 @@ namespace MatterHackers.MatterControl.DesignTools } else if (propertyValue is ChildrenSelector childSelector) { - rowContainer = CreateSettingsColumn(property); - rowContainer.AddChild(CreateSelector(childSelector, property.Item, theme)); + var showAsList = property.PropertyInfo.GetCustomAttributes(true).OfType().FirstOrDefault() != null; + if (showAsList) + { + UIField field = new ChildrenSelectorListField(property, theme); + + field.Initialize(0); + field.ValueChanged += (s, e) => + { + property.SetValue(new ChildrenSelector() { field.Value }); + object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties, undoBuffer)); + propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); + }; + + rowContainer = CreateSettingsRow(property, field); + } + else // show the subtarct editor for boolean subtract and subtract and replace + { + rowContainer = CreateSettingsColumn(property); + rowContainer.AddChild(CreateSelector(childSelector, property.Item, theme)); + } } else if (propertyValue is ImageBuffer imageBuffer) { diff --git a/MatterControl.csproj b/MatterControl.csproj index 66c00d4a2..334431a65 100644 --- a/MatterControl.csproj +++ b/MatterControl.csproj @@ -95,6 +95,7 @@ + @@ -283,6 +284,7 @@ + diff --git a/SlicerConfiguration/UIFields/ChildrenSelectorListField.cs b/SlicerConfiguration/UIFields/ChildrenSelectorListField.cs new file mode 100644 index 000000000..845e72ab0 --- /dev/null +++ b/SlicerConfiguration/UIFields/ChildrenSelectorListField.cs @@ -0,0 +1,107 @@ +/* +Copyright (c) 2018, Lars Brubaker, John Lewin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +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 MatterHackers.Agg.UI; +using MatterHackers.DataConverters3D; +using MatterHackers.Localizations; +using MatterHackers.MatterControl.DesignTools; +using MatterHackers.MatterControl.DesignTools.Operations; + +namespace MatterHackers.MatterControl.SlicerConfiguration +{ + public class ChildrenSelectorListField : UIField + { + private EditableProperty property; + private ThemeConfig theme; + private DropDownList dropDownList; + + public ChildrenSelectorListField(EditableProperty property, ThemeConfig theme) + { + this.property = property; + this.theme = theme; + } + + public override void Initialize(int tabIndex) + { + // Enum keyed on name to friendly name + List<(string key, string value)> names = null; + var selectedName = ""; + if (property.source is AlignObject3D item) + { + names = item.Children.Select(child => (child.ID, child.Name)).ToList(); + if (item.AnchorObjectSelector.Count == 1) + { + var selectedKey = item.AnchorObjectSelector[0]; + foreach(var name in names) + { + if(name.key == selectedKey) + { + selectedName = name.value; + } + } + } + } + + dropDownList = new DropDownList("Name".Localize(), theme.Colors.PrimaryTextColor, Direction.Down, pointSize: theme.DefaultFontSize) + { + BorderColor = theme.GetBorderColor(75) + }; + + var orderedItems = names.OrderBy(n => n.value); + + foreach (var orderItem in orderedItems) + { + MenuItem newItem = dropDownList.AddItem(orderItem.value, orderItem.key); + + var localOrderedItem = orderItem; + newItem.Selected += (sender, e) => + { + this.SetValue(localOrderedItem.key, true); + }; + } + + dropDownList.SelectedLabel = selectedName; + + this.Content = dropDownList; + } + + protected override void OnValueChanged(FieldChangedEventArgs fieldChangedEventArgs) + { + if (this.Value != dropDownList.SelectedValue) + { + dropDownList.SelectedValue = this.Value; + } + + base.OnValueChanged(fieldChangedEventArgs); + } + } +} diff --git a/Submodules/MatterSlice b/Submodules/MatterSlice index 635ee68e7..539644655 160000 --- a/Submodules/MatterSlice +++ b/Submodules/MatterSlice @@ -1 +1 @@ -Subproject commit 635ee68e79dba2a34176f9708cea2ea6e8be2ad2 +Subproject commit 53964465593f3611b1aedb6214209b7667f1d9f9 diff --git a/Submodules/agg-sharp b/Submodules/agg-sharp index a9fa98885..2edd3b61b 160000 --- a/Submodules/agg-sharp +++ b/Submodules/agg-sharp @@ -1 +1 @@ -Subproject commit a9fa98885be350b980f6808e7a9ce595fe750599 +Subproject commit 2edd3b61b87d29d1f70e9e0a36c5e25adf266726