2021-07-23 18:20:34 -07:00
/ *
Copyright ( c ) 2017 , 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 ;
2021-08-01 21:24:33 -07:00
using System.ComponentModel ;
2021-07-23 18:20:34 -07:00
using System.ComponentModel.DataAnnotations ;
using System.Linq ;
using System.Threading.Tasks ;
using ClipperLib ;
using MatterHackers.Agg ;
using MatterHackers.Agg.Image ;
using MatterHackers.Agg.Image.ThresholdFunctions ;
using MatterHackers.Agg.Transform ;
using MatterHackers.Agg.UI ;
using MatterHackers.Agg.VertexSource ;
using MatterHackers.DataConverters3D ;
using MatterHackers.Localizations ;
using MatterHackers.MarchingSquares ;
using MatterHackers.MatterControl.DesignTools.Operations ;
using MatterHackers.MatterControl.PartPreviewWindow ;
using Newtonsoft.Json ;
using Polygon = System . Collections . Generic . List < ClipperLib . IntPoint > ;
using Polygons = System . Collections . Generic . List < System . Collections . Generic . List < ClipperLib . IntPoint > > ;
namespace MatterHackers.MatterControl.DesignTools
{
2021-07-26 08:47:04 -07:00
[HideMeterialAndColor]
2021-08-01 21:24:33 -07:00
public class ImageToPathObject3D_2 : Object3D , IImageProvider , IPathObject , ISelectedEditorDraw , IObject3DControlsProvider , IPropertyGridModifier
2021-07-23 18:20:34 -07:00
{
public ImageToPathObject3D_2 ( )
{
Name = "Image to Path" . Localize ( ) ;
}
2021-08-04 17:59:05 -07:00
public enum AnalysisTypes
2021-07-23 18:20:34 -07:00
{
Transparency ,
Colors ,
Intensity ,
}
2021-08-16 18:11:18 -07:00
private ImageBuffer alphaImage ;
2021-07-27 18:12:17 -07:00
private ImageBuffer _image ;
2021-07-26 08:47:04 -07:00
/// <summary>
/// This is the image after it has been processed into an alpha image
/// </summary>
2021-07-23 18:20:34 -07:00
[JsonIgnore]
2021-07-26 08:47:04 -07:00
[ImageDisplay(Margin = new int[] { 30 , 3 , 30 , 3 } , MaxXSize = 400 , Stretch = true ) ]
public ImageBuffer Image
2021-07-23 18:20:34 -07:00
{
get
{
2021-08-01 21:24:33 -07:00
if ( _image = = null
& & SourceImage ! = null )
2021-07-27 09:38:41 -07:00
{
2021-07-27 18:12:17 -07:00
_image = new ImageBuffer ( SourceImage ) ;
2021-08-16 18:11:18 -07:00
alphaImage = new ImageBuffer ( SourceImage ) ;
2021-08-04 17:59:05 -07:00
Histogram . BuildHistogramFromImage ( SourceImage , AnalysisType ) ;
Histogram . RangeChanged + = ( s , e ) = >
2021-08-01 21:24:33 -07:00
{
2021-08-16 18:11:18 -07:00
Histogram . RebuildAlphaImage ( SourceImage , alphaImage , _image , AnalysisType ) ;
2021-08-01 21:24:33 -07:00
} ;
2021-08-04 17:59:05 -07:00
Histogram . EditComplete + = ( s , e ) = >
2021-07-27 18:12:17 -07:00
{
this . Invalidate ( InvalidateType . Properties ) ;
} ;
2021-08-01 21:24:33 -07:00
2021-08-04 17:59:05 -07:00
switch ( AnalysisType )
2021-08-01 21:24:33 -07:00
{
2021-08-04 17:59:05 -07:00
case AnalysisTypes . Intensity :
2021-08-17 09:42:34 -07:00
case AnalysisTypes . Colors :
2021-08-16 18:11:18 -07:00
Histogram . RebuildAlphaImage ( SourceImage , alphaImage , _image , AnalysisType ) ;
2021-08-01 21:24:33 -07:00
break ;
2021-08-04 17:59:05 -07:00
case AnalysisTypes . Transparency :
2021-08-01 21:24:33 -07:00
_image . CopyFrom ( SourceImage ) ;
break ;
}
2021-07-27 09:38:41 -07:00
}
2021-07-23 18:20:34 -07:00
2021-07-27 18:12:17 -07:00
return _image ;
2021-07-23 18:20:34 -07:00
}
set
{
}
}
2021-08-04 17:59:05 -07:00
private AnalysisTypes _featureDetector = AnalysisTypes . Intensity ;
2021-07-23 18:20:34 -07:00
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Tabs)]
2021-08-04 17:59:05 -07:00
public AnalysisTypes AnalysisType
2021-08-01 21:24:33 -07:00
{
get
{
return _featureDetector ;
}
set
{
if ( _featureDetector ! = value )
{
_featureDetector = value ;
2021-08-04 17:59:05 -07:00
var sourceImage = SourceImage ;
if ( sourceImage ! = null )
2021-08-01 21:24:33 -07:00
{
2021-08-04 17:59:05 -07:00
switch ( AnalysisType )
{
case AnalysisTypes . Intensity :
case AnalysisTypes . Colors :
Histogram . BuildHistogramFromImage ( sourceImage , AnalysisType ) ;
2021-08-16 18:11:18 -07:00
Histogram . RebuildAlphaImage ( sourceImage , alphaImage , Image , AnalysisType ) ;
2021-08-04 17:59:05 -07:00
break ;
case AnalysisTypes . Transparency :
Image ? . CopyFrom ( sourceImage ) ;
break ;
}
2021-08-01 21:24:33 -07:00
}
}
}
}
[DisplayName("")]
[ReadOnly(true)]
public string TransparencyMessage { get ; set ; } = "Your image is processed as is with no modifications. Transparent pixels are ignored, only opaque pixels are considered in feature detection." ;
2021-07-23 18:20:34 -07:00
[JsonIgnore]
2021-07-26 08:47:04 -07:00
private ImageBuffer SourceImage = > ( ( IImageProvider ) this . Descendants ( ) . Where ( i = > i is IImageProvider ) . FirstOrDefault ( ) ) ? . Image ;
2021-07-23 18:20:34 -07:00
2021-08-04 17:59:05 -07:00
public Histogram Histogram { get ; set ; } = new Histogram ( ) ;
2021-07-23 18:20:34 -07:00
public IVertexSource VertexSource { get ; set ; } = new VertexStorage ( ) ;
public void AddObject3DControls ( Object3DControlsLayer object3DControlsLayer )
{
object3DControlsLayer . AddControls ( ControlTypes . Standard2D ) ;
}
public void DrawEditor ( Object3DControlsLayer layer , List < Object3DView > transparentMeshes , DrawEventArgs e )
{
this . DrawPath ( ) ;
}
public override bool CanFlatten = > true ;
public override void Flatten ( UndoBuffer undoBuffer )
{
this . FlattenToPathObject ( undoBuffer ) ;
}
public void GenerateMarchingSquaresAndLines ( Action < double , string > progressReporter , ImageBuffer image , IThresholdFunction thresholdFunction )
{
if ( image ! = null )
{
// Regenerate outline
var marchingSquaresData = new MarchingSquaresByte (
image ,
thresholdFunction . ZeroColor ,
thresholdFunction . Threshold ,
0 ) ;
progressReporter ? . Invoke ( 0 , "Creating Outline" ) ;
marchingSquaresData . CreateLineSegments ( ) ;
progressReporter ? . Invoke ( . 1 , null ) ;
int pixelsToIntPointsScale = 1000 ;
var lineLoops = marchingSquaresData . CreateLineLoops ( pixelsToIntPointsScale ) ;
progressReporter ? . Invoke ( . 15 , null ) ;
var min = new IntPoint ( - 10 , - 10 ) ;
var max = new IntPoint ( 10 + image . Width * pixelsToIntPointsScale , 10 + image . Height * pixelsToIntPointsScale ) ;
var boundingPoly = new Polygon ( ) ;
boundingPoly . Add ( min ) ;
boundingPoly . Add ( new IntPoint ( min . X , max . Y ) ) ;
boundingPoly . Add ( max ) ;
boundingPoly . Add ( new IntPoint ( max . X , min . Y ) ) ;
// now clip the polygons to get the inside and outside polys
var clipper = new Clipper ( ) ;
clipper . AddPaths ( lineLoops , PolyType . ptSubject , true ) ;
clipper . AddPath ( boundingPoly , PolyType . ptClip , true ) ;
var polygonShape = new Polygons ( ) ;
progressReporter ? . Invoke ( . 3 , null ) ;
clipper . Execute ( ClipType . ctIntersection , polygonShape ) ;
progressReporter ? . Invoke ( . 55 , null ) ;
polygonShape = Clipper . CleanPolygons ( polygonShape , 100 ) ;
progressReporter ? . Invoke ( . 75 , null ) ;
VertexStorage rawVectorShape = polygonShape . PolygonToPathStorage ( ) ;
var aabb = this . VisibleMeshes ( ) . FirstOrDefault ( ) . GetAxisAlignedBoundingBox ( ) ;
var xScale = aabb . XSize / image . Width ;
var affine = Affine . NewScaling ( 1.0 / pixelsToIntPointsScale * xScale ) ;
affine * = Affine . NewTranslation ( - aabb . XSize / 2 , - aabb . YSize / 2 ) ;
rawVectorShape . transform ( affine ) ;
this . VertexSource = rawVectorShape ;
progressReporter ? . Invoke ( 1 , null ) ;
}
}
public override async void OnInvalidate ( InvalidateArgs invalidateArgs )
{
if ( invalidateArgs . InvalidateType . HasFlag ( InvalidateType . Image )
& & invalidateArgs . Source ! = this
& & ! RebuildLocked )
{
2021-08-16 09:18:36 -07:00
if ( AnalysisType ! = AnalysisTypes . Transparency )
{
Histogram . BuildHistogramFromImage ( SourceImage , AnalysisType ) ;
2021-08-16 18:11:18 -07:00
var _ = Image ; // call this to make sure it is built
Histogram . RebuildAlphaImage ( SourceImage , alphaImage , Image , AnalysisType ) ;
2021-08-16 09:18:36 -07:00
}
2021-07-23 18:20:34 -07:00
await Rebuild ( ) ;
}
else if ( ( invalidateArgs . InvalidateType . HasFlag ( InvalidateType . Properties ) & & invalidateArgs . Source = = this ) )
{
await Rebuild ( ) ;
}
else if ( SheetObject3D . NeedsRebuild ( this , invalidateArgs ) )
{
await Rebuild ( ) ;
}
base . OnInvalidate ( invalidateArgs ) ;
}
public override Task Rebuild ( )
{
this . DebugDepth ( "Rebuild" ) ;
var rebuildLock = RebuildLock ( ) ;
// now create a long running task to process the image
return ApplicationController . Instance . Tasks . Execute (
"Calculate Path" . Localize ( ) ,
null ,
( reporter , cancellationToken ) = >
{
var progressStatus = new ProgressStatus ( ) ;
2021-08-04 17:59:05 -07:00
switch ( AnalysisType )
2021-07-27 18:12:17 -07:00
{
2021-08-04 17:59:05 -07:00
case AnalysisTypes . Transparency :
2021-07-27 18:12:17 -07:00
this . GenerateMarchingSquaresAndLines (
( progress0to1 , status ) = >
{
progressStatus . Progress0To1 = progress0to1 ;
progressStatus . Status = status ;
reporter . Report ( progressStatus ) ;
} ,
SourceImage ,
new AlphaFunction ( ) ) ;
break ;
2021-08-16 09:18:36 -07:00
case AnalysisTypes . Colors :
2021-08-04 17:59:05 -07:00
case AnalysisTypes . Intensity :
2021-07-27 18:12:17 -07:00
this . GenerateMarchingSquaresAndLines (
( progress0to1 , status ) = >
{
progressStatus . Progress0To1 = progress0to1 ;
progressStatus . Status = status ;
reporter . Report ( progressStatus ) ;
} ,
2021-08-16 18:11:18 -07:00
alphaImage ,
2021-07-27 18:12:17 -07:00
new AlphaFunction ( ) ) ;
break ;
}
2021-07-23 18:20:34 -07:00
UiThread . RunOnIdle ( ( ) = >
{
rebuildLock . Dispose ( ) ;
Parent ? . Invalidate ( new InvalidateArgs ( this , InvalidateType . Path ) ) ;
} ) ;
return Task . CompletedTask ;
} ) ;
}
2021-08-01 21:24:33 -07:00
public void UpdateControls ( PublicPropertyChange change )
{
2021-08-04 17:59:05 -07:00
change . SetRowVisible ( nameof ( Histogram ) , ( ) = > AnalysisType ! = AnalysisTypes . Transparency ) ;
change . SetRowVisible ( nameof ( TransparencyMessage ) , ( ) = > AnalysisType = = AnalysisTypes . Transparency ) ;
2021-08-01 21:24:33 -07:00
}
2021-07-23 18:20:34 -07:00
}
}