2020-02-19 18:19:17 -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 ;
2020-02-19 22:07:09 -08:00
using System.ComponentModel ;
2020-02-19 18:19:17 -08:00
using System.Threading.Tasks ;
using g3 ;
2020-02-19 22:07:09 -08:00
using MatterHackers.Agg ;
2020-02-21 07:56:24 -08:00
using MatterHackers.Agg.UI ;
2020-02-19 18:19:17 -08:00
using MatterHackers.DataConverters3D ;
using MatterHackers.Localizations ;
using MatterHackers.MatterControl.DesignTools.Operations ;
using MatterHackers.PolygonMesh ;
2021-11-26 12:49:42 -08:00
using MatterHackers.PolygonMesh.Processors ;
2020-02-19 18:19:17 -08:00
namespace MatterHackers.MatterControl.DesignTools
{
2020-02-21 07:56:24 -08:00
public class DecimateObject3D : OperationSourceContainerObject3D , IPropertyGridModifier
2020-02-19 18:19:17 -08:00
{
public DecimateObject3D ( )
{
Name = "Reduce" . Localize ( ) ;
}
2020-02-21 18:18:47 -08:00
public enum ReductionMode
{
2021-09-20 09:20:22 -07:00
[EnumName("Count")]
2020-02-21 18:18:47 -08:00
Polygon_Count ,
2021-09-20 09:20:22 -07:00
[EnumName("Percent")]
2020-02-21 18:18:47 -08:00
Polygon_Percent
}
2020-05-21 16:29:45 -07:00
public override bool Persistable = > ApplicationController . Instance . UserHasPermission ( this ) ;
2021-09-20 09:20:22 -07:00
[EnumDisplay(Mode = EnumDisplayAttribute.PresentationMode.Buttons)]
2020-02-21 18:18:47 -08:00
public ReductionMode Mode { get ; set ; } = ReductionMode . Polygon_Percent ;
2020-02-19 22:07:09 -08:00
[ReadOnly(true)]
[Description("The original number of polygons.")]
2020-02-21 07:56:24 -08:00
public int SourcePolygonCount
2020-02-19 22:07:09 -08:00
{
get
{
var total = 0 ;
foreach ( var sourceItem in SourceContainer . VisibleMeshes ( ) )
{
total + = sourceItem . Mesh . Faces . Count ;
}
return total ;
}
2020-02-21 07:56:24 -08:00
set
{
}
}
2020-02-21 22:07:48 -08:00
[Description("The target number of polygons.")]
public int TargetCount { get ; set ; } = - 1 ;
[Description("The percentage of polygons to keep.")]
public double TargetPercent { get ; set ; } = 50 ;
[Description("Ensure that each reduced point is on the surface of the original mesh. This is not normally required and slows the computation significantly.")]
public bool MaintainSurface { get ; set ; } = false ;
2020-02-21 18:18:47 -08:00
[ReadOnly(true)]
2020-02-21 22:07:48 -08:00
[Description("The number of polygons determined by the percentage reduction.")]
[DisplayName("Final Count")]
public int CountAfterPercentReduction
2020-02-21 07:56:24 -08:00
{
2020-02-21 18:18:47 -08:00
get
{
return TargetCount ;
}
2020-02-19 18:19:17 -08:00
2020-02-21 18:18:47 -08:00
set
{
}
}
2020-02-21 07:56:24 -08:00
public Mesh Reduce ( Mesh inMesh , int targetCount )
2020-02-19 18:19:17 -08:00
{
2020-02-21 07:56:24 -08:00
var mesh = inMesh . ToDMesh3 ( ) ;
var reducer = new Reducer ( mesh ) ;
2020-02-19 22:07:09 -08:00
2020-02-19 18:19:17 -08:00
if ( MaintainSurface )
{
var tree = new DMeshAABBTree3 ( new DMesh3 ( mesh ) ) ;
tree . Build ( ) ;
2020-02-21 07:56:24 -08:00
var target = new MeshProjectionTarget ( tree . Mesh , tree ) ;
2020-02-19 18:19:17 -08:00
reducer . SetProjectionTarget ( target ) ;
reducer . ProjectionMode = Reducer . TargetProjectionMode . Inline ;
}
2020-02-21 07:56:24 -08:00
reducer . ReduceToTriangleCount ( Math . Max ( 4 , targetCount ) ) ;
2020-02-19 18:19:17 -08:00
return reducer . Mesh . ToMesh ( ) ;
}
public override Task Rebuild ( )
{
this . DebugDepth ( "Rebuild" ) ;
var rebuildLocks = this . RebuilLockAll ( ) ;
2020-02-19 22:07:09 -08:00
var valuesChanged = false ;
// check if we have be initialized
2020-02-21 18:18:47 -08:00
if ( Mode = = ReductionMode . Polygon_Count )
2020-02-21 07:56:24 -08:00
{
TargetCount = agg_basics . Clamp ( TargetCount , 4 , SourcePolygonCount , ref valuesChanged ) ;
2020-02-21 18:18:47 -08:00
TargetPercent = TargetCount / ( double ) SourcePolygonCount * 100 ;
2020-02-21 07:56:24 -08:00
}
else
{
TargetPercent = agg_basics . Clamp ( TargetPercent , 0 , 100 , ref valuesChanged ) ;
TargetCount = ( int ) ( SourcePolygonCount * TargetPercent / 100 ) ;
}
2020-02-27 08:56:39 -08:00
return TaskBuilder (
2020-02-19 18:19:17 -08:00
"Reduce" . Localize ( ) ,
( reporter , cancellationToken ) = >
{
SourceContainer . Visible = true ;
RemoveAllButSource ( ) ;
foreach ( var sourceItem in SourceContainer . VisibleMeshes ( ) )
{
var originalMesh = sourceItem . Mesh ;
2020-02-21 07:56:24 -08:00
var targetCount = ( int ) ( originalMesh . Faces . Count * TargetPercent / 100 ) ;
var reducedMesh = Reduce ( originalMesh , targetCount ) ;
2020-02-19 18:19:17 -08:00
var newMesh = new Object3D ( )
{
2020-04-21 21:13:32 -07:00
Mesh = reducedMesh ,
OwnerID = sourceItem . ID
2020-02-19 18:19:17 -08:00
} ;
2020-02-19 22:07:09 -08:00
newMesh . CopyProperties ( sourceItem , Object3DPropertyFlags . All ) ;
2020-02-19 18:19:17 -08:00
this . Children . Add ( newMesh ) ;
}
SourceContainer . Visible = false ;
2020-02-19 22:07:09 -08:00
2021-05-03 17:58:03 -07:00
UiThread . RunOnIdle ( ( ) = >
2020-02-19 22:07:09 -08:00
{
2021-05-03 17:58:03 -07:00
rebuildLocks . Dispose ( ) ;
2021-10-01 12:28:06 -07:00
Invalidate ( InvalidateType . DisplayValues ) ;
2021-05-03 17:58:03 -07:00
Parent ? . Invalidate ( new InvalidateArgs ( this , InvalidateType . Children ) ) ;
} ) ;
2020-02-19 22:07:09 -08:00
2020-02-19 18:19:17 -08:00
return Task . CompletedTask ;
} ) ;
}
2020-02-21 07:56:24 -08:00
public void UpdateControls ( PublicPropertyChange change )
{
if ( change . Context . GetEditRow ( nameof ( TargetPercent ) ) is GuiWidget percentWidget )
{
2020-02-21 18:18:47 -08:00
percentWidget . Visible = Mode = = ReductionMode . Polygon_Percent ;
}
2020-02-21 22:07:48 -08:00
if ( change . Context . GetEditRow ( nameof ( CountAfterPercentReduction ) ) is GuiWidget roTargetCountWidget )
2020-02-21 18:18:47 -08:00
{
roTargetCountWidget . Visible = Mode = = ReductionMode . Polygon_Percent ;
2020-02-21 07:56:24 -08:00
}
if ( change . Context . GetEditRow ( nameof ( TargetCount ) ) is GuiWidget countWidget )
{
2020-02-21 18:18:47 -08:00
countWidget . Visible = Mode = = ReductionMode . Polygon_Count ;
2020-02-21 07:56:24 -08:00
}
}
2020-02-19 18:19:17 -08:00
}
}