2018-03-10 16:32:04 -08:00
|
|
|
|
/*
|
|
|
|
|
|
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.ComponentModel;
|
|
|
|
|
|
using System.IO;
|
2019-01-29 15:37:06 -08:00
|
|
|
|
using System.Threading.Tasks;
|
2018-03-10 21:30:35 -08:00
|
|
|
|
using MatterHackers.Agg;
|
2018-03-10 16:32:04 -08:00
|
|
|
|
using MatterHackers.Agg.Font;
|
|
|
|
|
|
using MatterHackers.Agg.Platform;
|
|
|
|
|
|
using MatterHackers.Agg.Transform;
|
|
|
|
|
|
using MatterHackers.Agg.UI;
|
|
|
|
|
|
using MatterHackers.Agg.VertexSource;
|
|
|
|
|
|
using MatterHackers.DataConverters3D;
|
2018-06-25 17:03:54 -07:00
|
|
|
|
using MatterHackers.MatterControl.CustomWidgets;
|
2018-03-10 16:32:04 -08:00
|
|
|
|
using MatterHackers.MatterControl.DesignTools.Operations;
|
|
|
|
|
|
using MatterHackers.MatterControl.Plugins.BrailleBuilder;
|
2021-11-26 12:49:42 -08:00
|
|
|
|
using MatterHackers.PolygonMesh.Processors;
|
2018-03-10 16:32:04 -08:00
|
|
|
|
using MatterHackers.VectorMath;
|
|
|
|
|
|
using Newtonsoft.Json;
|
|
|
|
|
|
using Newtonsoft.Json.Converters;
|
|
|
|
|
|
|
|
|
|
|
|
namespace MatterHackers.MatterControl.DesignTools
|
|
|
|
|
|
{
|
2019-05-04 19:39:11 -07:00
|
|
|
|
[HideChildrenFromTreeView]
|
2021-04-22 14:24:34 -07:00
|
|
|
|
[WebPageLink("Website", "About Braille", "https://en.wikipedia.org/wiki/Braille")]
|
2019-05-04 19:39:11 -07:00
|
|
|
|
public class BrailleObject3D : Object3D
|
2018-03-10 16:32:04 -08:00
|
|
|
|
{
|
|
|
|
|
|
public BrailleObject3D()
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-03-13 08:26:54 -07:00
|
|
|
|
public BrailleObject3D(string textToEncode)
|
|
|
|
|
|
{
|
|
|
|
|
|
TextToEncode = textToEncode;
|
2019-01-29 15:37:06 -08:00
|
|
|
|
Rebuild();
|
2018-03-13 08:26:54 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-03-04 14:00:52 -08:00
|
|
|
|
public static async Task<BrailleObject3D> Create()
|
2018-03-10 16:32:04 -08:00
|
|
|
|
{
|
|
|
|
|
|
var item = new BrailleObject3D();
|
|
|
|
|
|
|
2019-03-04 14:00:52 -08:00
|
|
|
|
await item.Rebuild();
|
2018-03-10 16:32:04 -08:00
|
|
|
|
return item;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-05-09 13:35:38 -07:00
|
|
|
|
[DisplayName("Text")]
|
2018-03-10 16:32:04 -08:00
|
|
|
|
public string TextToEncode { get; set; } = "Braille";
|
|
|
|
|
|
|
2018-05-09 13:35:38 -07:00
|
|
|
|
[Description("Sets the depth of the tile the Braille is printed on")]
|
|
|
|
|
|
[DisplayName("Backing Depth")]
|
2018-03-10 21:30:35 -08:00
|
|
|
|
public double BaseHeight { get; set; } = 3;
|
2018-03-10 16:32:04 -08:00
|
|
|
|
|
|
|
|
|
|
[Description("Use Braille grade 2 (contractions)")]
|
|
|
|
|
|
public bool UseGrade2 { get; set; }
|
|
|
|
|
|
|
2018-05-09 13:35:38 -07:00
|
|
|
|
[Description("Choose to render as Braille or standard text. This can help show how grade 2 works.")]
|
2018-03-10 16:32:04 -08:00
|
|
|
|
public bool RenderAsBraille { get; set; } = true;
|
|
|
|
|
|
|
2018-05-09 13:35:38 -07:00
|
|
|
|
[Description("Create a hook so the Braille can be hung from a necklace or keychain.")]
|
2018-03-13 17:17:28 -07:00
|
|
|
|
public bool AddHook { get; set; }
|
|
|
|
|
|
|
2020-11-25 07:39:36 -08:00
|
|
|
|
static TypeFace typeFace = TypeFace.LoadFrom(StaticData.Instance.ReadAllText(Path.Combine("Fonts", "Braille.svg")));
|
2018-03-10 16:32:04 -08:00
|
|
|
|
|
2019-01-30 08:28:24 -08:00
|
|
|
|
public override async void OnInvalidate(InvalidateArgs invalidateType)
|
2018-03-10 16:32:04 -08:00
|
|
|
|
{
|
2019-01-28 14:19:40 -08:00
|
|
|
|
if (invalidateType.InvalidateType.HasFlag(InvalidateType.Properties)
|
2018-06-25 17:03:54 -07:00
|
|
|
|
&& invalidateType.Source == this)
|
2018-03-13 08:26:54 -07:00
|
|
|
|
{
|
2019-01-30 08:28:24 -08:00
|
|
|
|
await Rebuild();
|
2018-03-13 08:26:54 -07:00
|
|
|
|
}
|
2019-01-29 15:37:06 -08:00
|
|
|
|
|
|
|
|
|
|
base.OnInvalidate(invalidateType);
|
2018-06-25 17:03:54 -07:00
|
|
|
|
}
|
2018-03-13 08:26:54 -07:00
|
|
|
|
|
2019-03-01 18:19:16 -08:00
|
|
|
|
public override Task Rebuild()
|
2018-06-25 17:03:54 -07:00
|
|
|
|
{
|
|
|
|
|
|
using (RebuildLock())
|
2018-03-10 16:32:04 -08:00
|
|
|
|
{
|
2019-04-19 09:52:49 -07:00
|
|
|
|
using (new CenterAndHeightMaintainer(this))
|
2019-01-29 15:13:16 -08:00
|
|
|
|
{
|
|
|
|
|
|
this.Children.Modify(list =>
|
2021-05-03 17:58:03 -07:00
|
|
|
|
{
|
|
|
|
|
|
list.Clear();
|
|
|
|
|
|
});
|
2018-06-25 17:03:54 -07:00
|
|
|
|
|
2019-01-29 15:13:16 -08:00
|
|
|
|
var brailleText = TextToEncode;
|
|
|
|
|
|
if (UseGrade2)
|
|
|
|
|
|
{
|
|
|
|
|
|
brailleText = BrailleGrade2.ConvertString(brailleText);
|
|
|
|
|
|
}
|
2018-03-10 16:32:04 -08:00
|
|
|
|
|
2019-01-29 15:13:16 -08:00
|
|
|
|
double pointSize = 18.5;
|
|
|
|
|
|
double pointsToMm = 0.352778;
|
|
|
|
|
|
IObject3D textObject = new Object3D();
|
|
|
|
|
|
var offest = 0.0;
|
2018-03-10 16:32:04 -08:00
|
|
|
|
|
2019-01-29 15:13:16 -08:00
|
|
|
|
TypeFacePrinter textPrinter;
|
2018-06-25 17:03:54 -07:00
|
|
|
|
if (RenderAsBraille)
|
|
|
|
|
|
{
|
2019-01-29 15:13:16 -08:00
|
|
|
|
textPrinter = new TypeFacePrinter(brailleText, new StyledTypeFace(typeFace, pointSize));
|
2018-06-25 17:03:54 -07:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2023-12-03 19:00:41 -08:00
|
|
|
|
textPrinter = new TypeFacePrinter(brailleText, new StyledTypeFace(ApplicationController.Instance.GetTypeFace("Liberation_Mono"), pointSize));
|
2018-06-25 17:03:54 -07:00
|
|
|
|
}
|
2018-03-13 11:52:23 -07:00
|
|
|
|
|
2019-01-29 15:13:16 -08:00
|
|
|
|
foreach (var letter in brailleText.ToCharArray())
|
|
|
|
|
|
{
|
|
|
|
|
|
IObject3D letterObject;
|
|
|
|
|
|
TypeFacePrinter letterPrinter;
|
|
|
|
|
|
if (RenderAsBraille)
|
|
|
|
|
|
{
|
|
|
|
|
|
letterPrinter = new TypeFacePrinter(letter.ToString(), new StyledTypeFace(typeFace, pointSize));
|
|
|
|
|
|
var scalledLetterPrinter = new VertexSourceApplyTransform(letterPrinter, Affine.NewScaling(pointsToMm));
|
2018-03-13 11:52:23 -07:00
|
|
|
|
|
2019-01-29 15:13:16 -08:00
|
|
|
|
// add all the spheres to letterObject
|
|
|
|
|
|
letterObject = new Object3D();
|
2018-03-13 11:52:23 -07:00
|
|
|
|
|
2019-01-29 15:13:16 -08:00
|
|
|
|
var vertexCount = 0;
|
|
|
|
|
|
var positionSum = Vector2.Zero;
|
|
|
|
|
|
var lastPosition = Vector2.Zero;
|
|
|
|
|
|
// find each dot outline and get it's center and place a sphere there
|
|
|
|
|
|
foreach (var vertex in scalledLetterPrinter.Vertices())
|
|
|
|
|
|
{
|
2023-10-31 15:13:04 -07:00
|
|
|
|
switch (vertex.Command)
|
2019-01-29 15:13:16 -08:00
|
|
|
|
{
|
2023-10-31 15:13:04 -07:00
|
|
|
|
case Agg.FlagsAndCommand.Stop:
|
|
|
|
|
|
case Agg.FlagsAndCommand.EndPoly:
|
|
|
|
|
|
case Agg.FlagsAndCommand.FlagClose:
|
|
|
|
|
|
case Agg.FlagsAndCommand.MoveTo:
|
2019-01-29 15:13:16 -08:00
|
|
|
|
if (vertexCount > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
var center = positionSum / vertexCount;
|
|
|
|
|
|
double radius = 1.44 / 2;// (center - lastPosition).Length;
|
|
|
|
|
|
var sphere = new HalfSphereObject3D(radius * 2, 15)
|
|
|
|
|
|
{
|
|
|
|
|
|
Color = Color.LightBlue
|
|
|
|
|
|
};
|
|
|
|
|
|
sphere.Translate(center.X, center.Y);
|
|
|
|
|
|
letterObject.Children.Add(sphere);
|
|
|
|
|
|
}
|
|
|
|
|
|
vertexCount = 0;
|
|
|
|
|
|
positionSum = Vector2.Zero;
|
|
|
|
|
|
break;
|
2023-10-31 15:13:04 -07:00
|
|
|
|
case Agg.FlagsAndCommand.Curve3:
|
|
|
|
|
|
case Agg.FlagsAndCommand.Curve4:
|
|
|
|
|
|
case Agg.FlagsAndCommand.LineTo:
|
2019-01-29 15:13:16 -08:00
|
|
|
|
vertexCount++;
|
2023-10-31 15:13:04 -07:00
|
|
|
|
lastPosition = vertex.Position;
|
2019-01-29 15:13:16 -08:00
|
|
|
|
positionSum += lastPosition;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2023-12-03 19:00:41 -08:00
|
|
|
|
letterPrinter = new TypeFacePrinter(letter.ToString(), new StyledTypeFace(ApplicationController.Instance.GetTypeFace("Liberation_Mono"), pointSize));
|
2019-01-29 15:13:16 -08:00
|
|
|
|
var scalledLetterPrinter = new VertexSourceApplyTransform(letterPrinter, Affine.NewScaling(pointsToMm));
|
|
|
|
|
|
letterObject = new Object3D()
|
|
|
|
|
|
{
|
|
|
|
|
|
Mesh = VertexSourceToMesh.Extrude(scalledLetterPrinter, 1),
|
|
|
|
|
|
Color = Color.LightBlue
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
2018-03-10 21:30:35 -08:00
|
|
|
|
|
2019-01-29 15:13:16 -08:00
|
|
|
|
letterObject.Matrix = Matrix4X4.CreateTranslation(offest, 0, 0);
|
|
|
|
|
|
textObject.Children.Add(letterObject);
|
2018-03-10 16:32:04 -08:00
|
|
|
|
|
2019-01-29 15:13:16 -08:00
|
|
|
|
offest += letterPrinter.GetSize(letter.ToString()).X * pointsToMm;
|
|
|
|
|
|
}
|
2018-03-13 17:17:28 -07:00
|
|
|
|
|
2019-01-29 15:13:16 -08:00
|
|
|
|
// add a plate under the dots
|
|
|
|
|
|
var padding = .9 * pointSize * pointsToMm / 2;
|
|
|
|
|
|
var size = textPrinter.LocalBounds * pointsToMm;
|
2018-03-13 17:17:28 -07:00
|
|
|
|
|
2019-01-29 15:13:16 -08:00
|
|
|
|
// make the base
|
|
|
|
|
|
var basePath = new VertexStorage();
|
|
|
|
|
|
basePath.MoveTo(0, 0);
|
|
|
|
|
|
basePath.LineTo(size.Width + padding, 0);
|
|
|
|
|
|
basePath.LineTo(size.Width + padding, size.Height + padding);
|
|
|
|
|
|
basePath.LineTo(padding, size.Height + padding);
|
|
|
|
|
|
basePath.LineTo(0, size.Height);
|
2018-06-25 17:03:54 -07:00
|
|
|
|
|
2019-01-29 15:13:16 -08:00
|
|
|
|
IObject3D basePlate = new Object3D()
|
2018-06-25 17:03:54 -07:00
|
|
|
|
{
|
2019-01-29 15:13:16 -08:00
|
|
|
|
Mesh = VertexSourceToMesh.Extrude(basePath, BaseHeight)
|
2018-06-25 17:03:54 -07:00
|
|
|
|
};
|
|
|
|
|
|
|
2022-02-26 22:21:29 -08:00
|
|
|
|
basePlate = new AlignObject3D_2(basePlate, FaceAlign.Top, textObject, FaceAlign.Bottom, 0, 0, .01);
|
|
|
|
|
|
basePlate = new AlignObject3D_2(basePlate, FaceAlign.Left | FaceAlign.Front,
|
2019-01-29 15:13:16 -08:00
|
|
|
|
size.Left - padding / 2,
|
|
|
|
|
|
size.Bottom - padding / 2);
|
|
|
|
|
|
this.Children.Add(basePlate);
|
2018-03-13 17:17:28 -07:00
|
|
|
|
|
2019-01-29 15:13:16 -08:00
|
|
|
|
basePlate.Matrix *= Matrix4X4.CreateRotationX(MathHelper.Tau / 4);
|
|
|
|
|
|
|
|
|
|
|
|
// add an optional chain hook
|
|
|
|
|
|
if (AddHook)
|
2018-06-25 17:03:54 -07:00
|
|
|
|
{
|
2019-01-29 15:13:16 -08:00
|
|
|
|
// x 10 to make it smoother
|
|
|
|
|
|
double edgeWidth = 3;
|
|
|
|
|
|
double height = basePlate.ZSize();
|
|
|
|
|
|
IVertexSource leftSideObject = new RoundedRect(0, 0, height / 2, height, 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
ResolutionScale = 10
|
|
|
|
|
|
};
|
2018-03-13 17:17:28 -07:00
|
|
|
|
|
2019-01-29 15:13:16 -08:00
|
|
|
|
IVertexSource cicleObject = new Ellipse(0, 0, height / 2, height / 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
ResolutionScale = 10
|
|
|
|
|
|
};
|
2018-03-13 17:17:28 -07:00
|
|
|
|
|
2019-01-29 15:13:16 -08:00
|
|
|
|
cicleObject = new Align2D(cicleObject, Side2D.Left | Side2D.Bottom, leftSideObject, Side2D.Left | Side2D.Bottom, -.01);
|
|
|
|
|
|
IVertexSource holeObject = new Ellipse(0, 0, height / 2 - edgeWidth, height / 2 - edgeWidth)
|
|
|
|
|
|
{
|
|
|
|
|
|
ResolutionScale = 10
|
|
|
|
|
|
};
|
|
|
|
|
|
holeObject = new SetCenter2D(holeObject, cicleObject.GetBounds().Center);
|
2018-03-13 17:17:28 -07:00
|
|
|
|
|
2019-01-29 15:13:16 -08:00
|
|
|
|
IVertexSource hookPath = leftSideObject.Plus(cicleObject);
|
|
|
|
|
|
hookPath = hookPath.Minus(holeObject);
|
2018-06-25 17:03:54 -07:00
|
|
|
|
|
2019-01-29 15:13:16 -08:00
|
|
|
|
IObject3D chainHook = new Object3D()
|
|
|
|
|
|
{
|
|
|
|
|
|
Mesh = VertexSourceToMesh.Extrude(hookPath, BaseHeight),
|
|
|
|
|
|
Matrix = Matrix4X4.CreateRotationX(MathHelper.Tau / 4)
|
|
|
|
|
|
};
|
2018-03-13 17:17:28 -07:00
|
|
|
|
|
2022-02-26 22:21:29 -08:00
|
|
|
|
chainHook = new AlignObject3D_2(chainHook, FaceAlign.Left | FaceAlign.Bottom | FaceAlign.Back, basePlate, FaceAlign.Right | FaceAlign.Bottom | FaceAlign.Back, -.01);
|
2018-03-10 16:54:43 -08:00
|
|
|
|
|
2019-01-29 15:13:16 -08:00
|
|
|
|
this.Children.Add(chainHook);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// add the object that is the dots
|
|
|
|
|
|
this.Children.Add(textObject);
|
|
|
|
|
|
textObject.Matrix *= Matrix4X4.CreateRotationX(MathHelper.Tau / 4);
|
2018-06-25 17:03:54 -07:00
|
|
|
|
}
|
2018-03-10 16:32:04 -08:00
|
|
|
|
}
|
2018-06-25 17:03:54 -07:00
|
|
|
|
|
2021-12-05 22:01:50 -08:00
|
|
|
|
this.CancelAllParentBuilding();
|
2019-02-13 15:45:33 -08:00
|
|
|
|
Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Children));
|
2021-12-05 22:01:50 -08:00
|
|
|
|
return base.Rebuild();
|
2018-03-10 16:32:04 -08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|