Weapos Rejiggered
Modified weapon scripts to eliminate specialized projectile behavior - allows charged weapons to have projectiles and modify weapon stats when charged Added machine gun weapon & VFX Moved third party code to Plugins folder to improve compile times
This commit is contained in:
parent
2a0fe34611
commit
dbc522a224
2105 changed files with 7033 additions and 1141 deletions
|
|
@ -7,6 +7,7 @@ public class ConstantLaser : MonoBehaviour
|
|||
public AnimationCurve StartCurve;
|
||||
public AnimationCurve EndCurve;
|
||||
public AnimationCurve IntensityCurve;
|
||||
public float WidthMultiplier = 1;
|
||||
public float StartDuration;
|
||||
public float FadeDuration;
|
||||
public float CycleDuration;
|
||||
|
|
@ -40,7 +41,7 @@ public class ConstantLaser : MonoBehaviour
|
|||
if (_stopping)
|
||||
{
|
||||
var lerp = (Time.time - _startTime) / FadeDuration;
|
||||
LineRenderer.widthMultiplier = EndCurve.Evaluate(lerp) * _stopIntensity;
|
||||
LineRenderer.widthMultiplier = EndCurve.Evaluate(lerp) * _stopIntensity * WidthMultiplier;
|
||||
|
||||
if (lerp > 1)
|
||||
{
|
||||
|
|
@ -59,11 +60,11 @@ public class ConstantLaser : MonoBehaviour
|
|||
_cycleStartTime = Time.time;
|
||||
}
|
||||
_intensity = IntensityCurve.Evaluate((Time.time - _cycleStartTime) / CycleDuration % CycleDuration);
|
||||
LineRenderer.widthMultiplier = _intensity;
|
||||
LineRenderer.widthMultiplier = _intensity * WidthMultiplier;
|
||||
}
|
||||
else
|
||||
{
|
||||
LineRenderer.widthMultiplier = StartCurve.Evaluate(lerp);
|
||||
LineRenderer.widthMultiplier = StartCurve.Evaluate(lerp) * WidthMultiplier;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,30 +6,29 @@ public class GuidedProjectileManager : InstantWeaponEffectManager
|
|||
{
|
||||
public Prototype ProjectilePrototype;
|
||||
|
||||
public override void Fire(WeaponData data, EquippedItem item, EntityInstance source, EntityInstance target)
|
||||
public override void Fire(InstantWeapon weapon, EquippedItem item, EntityInstance source, EntityInstance target)
|
||||
{
|
||||
if(data is LauncherData launcher)
|
||||
if(weapon.Data is LauncherData launcher)
|
||||
{
|
||||
var p = ProjectilePrototype.Instantiate<GuidedProjectile>();
|
||||
p.Source = source.Transform;
|
||||
p.SourceEntity = source.Entity;
|
||||
p.Target = target.Transform;
|
||||
p.Frequency = launcher.DodgeFrequency;
|
||||
p.Thrust = source.Entity.ItemManager.Evaluate(launcher.Thrust, item.EquippableItem, source.Entity);
|
||||
var hp = source.Entity.Hardpoints[item.Position.x, item.Position.y];
|
||||
var barrel = source.GetBarrel(hp);
|
||||
p.StartPosition = p.transform.position = barrel.position;
|
||||
p.Range = source.Entity.ItemManager.Evaluate(data.Range, item.EquippableItem, source.Entity);
|
||||
p.Velocity = barrel.forward * source.Entity.ItemManager.Evaluate(launcher.LaunchSpeed, item.EquippableItem, source.Entity);
|
||||
p.Damage = source.Entity.ItemManager.Evaluate(data.Damage, item.EquippableItem, source.Entity);
|
||||
p.Penetration = source.Entity.ItemManager.Evaluate(data.Penetration, item.EquippableItem, source.Entity);
|
||||
p.Spread = source.Entity.ItemManager.Evaluate(data.DamageSpread, item.EquippableItem, source.Entity);
|
||||
p.DamageType = data.DamageType;
|
||||
p.Damage = weapon.Damage;
|
||||
p.Range = weapon.Range;
|
||||
p.Penetration = weapon.Penetration;
|
||||
p.Spread = weapon.DamageSpread;
|
||||
p.DamageType = weapon.WeaponData.DamageType;
|
||||
p.GuidanceCurve = launcher.GuidanceCurve.ToCurve();
|
||||
p.LiftCurve = launcher.LiftCurve.ToCurve();
|
||||
p.ThrustCurve = launcher.ThrustCurve.ToCurve();
|
||||
p.Velocity = barrel.forward * weapon.Velocity;
|
||||
p.Thrust = source.Entity.ItemManager.Evaluate(launcher.Thrust, item.EquippableItem, source.Entity);
|
||||
p.TopSpeed = source.Entity.ItemManager.Evaluate(launcher.Velocity, item.EquippableItem, source.Entity);
|
||||
p.TopSpeed = source.Entity.ItemManager.Evaluate(launcher.MissileVelocity, item.EquippableItem, source.Entity);
|
||||
}
|
||||
else Debug.LogError($"Weapon {item.EquippableItem.Name} linked to {name} effect, but is not a Launcher!");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
public class HitscanManager : InstantWeaponEffectManager
|
||||
{
|
||||
public Prototype Prototype;
|
||||
public override void Fire(WeaponData data, EquippedItem item, EntityInstance source, EntityInstance target)
|
||||
public override void Fire(InstantWeapon weapon, EquippedItem item, EntityInstance source, EntityInstance target)
|
||||
{
|
||||
var p = Prototype.Instantiate<HitscanEffect>();
|
||||
p.SourceEntity = source.Entity;
|
||||
|
|
@ -10,11 +10,10 @@ public class HitscanManager : InstantWeaponEffectManager
|
|||
var t = p.transform;
|
||||
t.position = barrel.position;
|
||||
t.forward = barrel.forward;
|
||||
p.Range = source.Entity.ItemManager.Evaluate(data.Range, item.EquippableItem, source.Entity);
|
||||
p.Damage = source.Entity.ItemManager.Evaluate(data.Damage, item.EquippableItem, source.Entity);
|
||||
p.Penetration = source.Entity.ItemManager.Evaluate(data.Penetration, item.EquippableItem, source.Entity);
|
||||
p.Spread = source.Entity.ItemManager.Evaluate(data.DamageSpread, item.EquippableItem, source.Entity);
|
||||
p.DamageType = data.DamageType;
|
||||
p.Damage = weapon.Damage;
|
||||
p.Penetration = weapon.Penetration;
|
||||
p.Spread = weapon.DamageSpread;
|
||||
p.DamageType = weapon.WeaponData.DamageType;
|
||||
p.Zone = source.Entity.Zone;
|
||||
p.Fire();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,5 +4,5 @@ using UnityEngine;
|
|||
|
||||
public abstract class InstantWeaponEffectManager : MonoBehaviour
|
||||
{
|
||||
public abstract void Fire(WeaponData data, EquippedItem item, EntityInstance source, EntityInstance target);
|
||||
public abstract void Fire(InstantWeapon weapon, EquippedItem item, EntityInstance source, EntityInstance target);
|
||||
}
|
||||
|
|
@ -6,20 +6,20 @@ public class LaserManager : InstantWeaponEffectManager
|
|||
{
|
||||
public Prototype ProjectilePrototype;
|
||||
|
||||
public override void Fire(WeaponData data, EquippedItem item, EntityInstance source, EntityInstance target)
|
||||
public override void Fire(InstantWeapon weapon, EquippedItem item, EntityInstance source, EntityInstance target)
|
||||
{
|
||||
var p = ProjectilePrototype.Instantiate<Laser>();
|
||||
p.SourceEntity = source.Entity;
|
||||
var hp = source.Entity.Hardpoints[item.Position.x, item.Position.y];
|
||||
var barrel = source.GetBarrel(hp);
|
||||
p.Range = source.Entity.ItemManager.Evaluate(data.Range, item.EquippableItem, source.Entity);
|
||||
var t = p.transform;
|
||||
t.SetParent(barrel);
|
||||
t.forward = barrel.forward;
|
||||
t.position = barrel.position;
|
||||
p.Damage = source.Entity.ItemManager.Evaluate(data.Damage, item.EquippableItem, source.Entity);
|
||||
p.Penetration = source.Entity.ItemManager.Evaluate(data.Penetration, item.EquippableItem, source.Entity);
|
||||
p.Spread = source.Entity.ItemManager.Evaluate(data.DamageSpread, item.EquippableItem, source.Entity);
|
||||
p.DamageType = data.DamageType;
|
||||
p.SourceEntity = source.Entity;
|
||||
p.Damage = weapon.Damage;
|
||||
p.Range = weapon.Range;
|
||||
p.Penetration = weapon.Penetration;
|
||||
p.Spread = weapon.DamageSpread;
|
||||
p.DamageType = weapon.WeaponData.DamageType;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,8 +42,11 @@ public class Projectile : MonoBehaviour
|
|||
|
||||
if(_alive)
|
||||
{
|
||||
var position = transform.position;
|
||||
var t = transform;
|
||||
var position = t.position;
|
||||
Velocity += Vector3.up * (Gravity * Time.deltaTime);
|
||||
var forward = Velocity.normalized;
|
||||
t.forward = forward;
|
||||
var ray = new Ray(position, Velocity);
|
||||
if (Physics.Raycast(ray, out var hit, Velocity.magnitude * Time.deltaTime, 1))
|
||||
{
|
||||
|
|
@ -52,7 +55,7 @@ public class Projectile : MonoBehaviour
|
|||
{
|
||||
if (hull.Entity != SourceEntity)
|
||||
{
|
||||
hull.SendHit(Damage, Penetration, Spread, DamageType, SourceEntity, hit, Velocity.normalized);
|
||||
hull.SendHit(Damage, Penetration, Spread, DamageType, SourceEntity, hit, forward);
|
||||
transform.position = hit.point;
|
||||
StartCoroutine(Kill());
|
||||
}
|
||||
|
|
@ -62,6 +65,14 @@ public class Projectile : MonoBehaviour
|
|||
StartCoroutine(Kill());
|
||||
return;
|
||||
}
|
||||
|
||||
if (HitEffect != null)
|
||||
{
|
||||
var ht = HitEffect.Instantiate<Transform>();
|
||||
ht.SetParent(hit.collider.transform);
|
||||
ht.position = hit.point;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
transform.position += Velocity * Time.deltaTime;
|
||||
|
|
|
|||
|
|
@ -5,27 +5,28 @@ using UnityEngine;
|
|||
public class ProjectileManager : InstantWeaponEffectManager
|
||||
{
|
||||
public Prototype ProjectilePrototype;
|
||||
public bool InheritVelocity;
|
||||
|
||||
public override void Fire(WeaponData data, EquippedItem item, EntityInstance source, EntityInstance target)
|
||||
public override void Fire(InstantWeapon weapon, EquippedItem item, EntityInstance source, EntityInstance target)
|
||||
{
|
||||
if(data is ProjectileWeaponData projectile)
|
||||
{
|
||||
var p = ProjectilePrototype.Instantiate<Projectile>();
|
||||
p.SourceEntity = source.Entity;
|
||||
var hp = source.Entity.Hardpoints[item.Position.x, item.Position.y];
|
||||
var barrel = source.GetBarrel(hp);
|
||||
p.Range = source.Entity.ItemManager.Evaluate(data.Range, item.EquippableItem, source.Entity);
|
||||
p.StartPosition = p.transform.position = barrel.position;
|
||||
var angle = source.Entity.ItemManager.Evaluate(projectile.Spread, item.EquippableItem, source.Entity) / 2;
|
||||
p.Velocity = (Quaternion.Euler(Random.Range(-angle, angle), Random.Range(-angle, angle), Random.Range(-angle, angle)) * barrel.forward) *
|
||||
source.Entity.ItemManager.Evaluate(projectile.Velocity, item.EquippableItem, source.Entity);
|
||||
p.Damage = source.Entity.ItemManager.Evaluate(data.Damage, item.EquippableItem, source.Entity);
|
||||
p.Penetration = source.Entity.ItemManager.Evaluate(data.Penetration, item.EquippableItem, source.Entity);
|
||||
p.Spread = source.Entity.ItemManager.Evaluate(data.DamageSpread, item.EquippableItem, source.Entity);
|
||||
p.DamageType = data.DamageType;
|
||||
p.Zone = source.Entity.Zone;
|
||||
p.Trail.Clear();
|
||||
}
|
||||
else Debug.LogError($"Weapon {item.EquippableItem.Name} linked to {name} effect, but is not a Projectile Weapon!");
|
||||
var p = ProjectilePrototype.Instantiate<Projectile>();
|
||||
var hp = source.Entity.Hardpoints[item.Position.x, item.Position.y];
|
||||
var barrel = source.GetBarrel(hp);
|
||||
var angle = weapon.Spread / 2;
|
||||
p.SourceEntity = source.Entity;
|
||||
p.StartPosition = p.transform.position = barrel.position;
|
||||
p.Velocity = Quaternion.Euler(
|
||||
Random.Range(-angle, angle),
|
||||
Random.Range(-angle, angle),
|
||||
Random.Range(-angle, angle)) *
|
||||
barrel.forward *
|
||||
weapon.Velocity;
|
||||
p.Damage = weapon.Damage;
|
||||
p.Range = weapon.Range;
|
||||
p.Penetration = weapon.Penetration;
|
||||
p.Spread = weapon.DamageSpread;
|
||||
p.DamageType = weapon.WeaponData.DamageType;
|
||||
p.Zone = source.Entity.Zone;
|
||||
p.Trail.Clear();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ec3325b6b8b1a504b8de9e3553bce449
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 48b22515742194f478ce494a467011b0
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,129 +0,0 @@
|
|||
//
|
||||
// Noise Shader Library for Unity - https://github.com/keijiro/NoiseShader
|
||||
//
|
||||
// Original work (webgl-noise) Copyright (C) 2011 Stefan Gustavson
|
||||
// Translation and modification was made by Keijiro Takahashi.
|
||||
//
|
||||
// This shader is based on the webgl-noise GLSL shader. For further details
|
||||
// of the original shader, please see the following description from the
|
||||
// original source code.
|
||||
//
|
||||
|
||||
//
|
||||
// GLSL textureless classic 2D noise "cnoise",
|
||||
// with an RSL-style periodic variant "pnoise".
|
||||
// Author: Stefan Gustavson (stefan.gustavson@liu.se)
|
||||
// Version: 2011-08-22
|
||||
//
|
||||
// Many thanks to Ian McEwan of Ashima Arts for the
|
||||
// ideas for permutation and gradient selection.
|
||||
//
|
||||
// Copyright (c) 2011 Stefan Gustavson. All rights reserved.
|
||||
// Distributed under the MIT license. See LICENSE file.
|
||||
// https://github.com/ashima/webgl-noise
|
||||
//
|
||||
|
||||
float4 mod(float4 x, float4 y)
|
||||
{
|
||||
return x - y * floor(x / y);
|
||||
}
|
||||
|
||||
float4 mod289(float4 x)
|
||||
{
|
||||
return x - floor(x / 289.0) * 289.0;
|
||||
}
|
||||
|
||||
float4 permute(float4 x)
|
||||
{
|
||||
return mod289(((x*34.0)+1.0)*x);
|
||||
}
|
||||
|
||||
float4 taylorInvSqrt(float4 r)
|
||||
{
|
||||
return (float4)1.79284291400159 - r * 0.85373472095314;
|
||||
}
|
||||
|
||||
float2 fade(float2 t) {
|
||||
return t*t*t*(t*(t*6.0-15.0)+10.0);
|
||||
}
|
||||
|
||||
// Classic Perlin noise
|
||||
float cnoise(float2 P)
|
||||
{
|
||||
float4 Pi = floor(P.xyxy) + float4(0.0, 0.0, 1.0, 1.0);
|
||||
float4 Pf = frac (P.xyxy) - float4(0.0, 0.0, 1.0, 1.0);
|
||||
Pi = mod289(Pi); // To avoid truncation effects in permutation
|
||||
float4 ix = Pi.xzxz;
|
||||
float4 iy = Pi.yyww;
|
||||
float4 fx = Pf.xzxz;
|
||||
float4 fy = Pf.yyww;
|
||||
|
||||
float4 i = permute(permute(ix) + iy);
|
||||
|
||||
float4 gx = frac(i / 41.0) * 2.0 - 1.0 ;
|
||||
float4 gy = abs(gx) - 0.5 ;
|
||||
float4 tx = floor(gx + 0.5);
|
||||
gx = gx - tx;
|
||||
|
||||
float2 g00 = float2(gx.x,gy.x);
|
||||
float2 g10 = float2(gx.y,gy.y);
|
||||
float2 g01 = float2(gx.z,gy.z);
|
||||
float2 g11 = float2(gx.w,gy.w);
|
||||
|
||||
float4 norm = taylorInvSqrt(float4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11)));
|
||||
g00 *= norm.x;
|
||||
g01 *= norm.y;
|
||||
g10 *= norm.z;
|
||||
g11 *= norm.w;
|
||||
|
||||
float n00 = dot(g00, float2(fx.x, fy.x));
|
||||
float n10 = dot(g10, float2(fx.y, fy.y));
|
||||
float n01 = dot(g01, float2(fx.z, fy.z));
|
||||
float n11 = dot(g11, float2(fx.w, fy.w));
|
||||
|
||||
float2 fade_xy = fade(Pf.xy);
|
||||
float2 n_x = lerp(float2(n00, n01), float2(n10, n11), fade_xy.x);
|
||||
float n_xy = lerp(n_x.x, n_x.y, fade_xy.y);
|
||||
return 2.3 * n_xy;
|
||||
}
|
||||
|
||||
// Classic Perlin noise, periodic variant
|
||||
float pnoise(float2 P, float2 rep)
|
||||
{
|
||||
float4 Pi = floor(P.xyxy) + float4(0.0, 0.0, 1.0, 1.0);
|
||||
float4 Pf = frac (P.xyxy) - float4(0.0, 0.0, 1.0, 1.0);
|
||||
Pi = mod(Pi, rep.xyxy); // To create noise with explicit period
|
||||
Pi = mod289(Pi); // To avoid truncation effects in permutation
|
||||
float4 ix = Pi.xzxz;
|
||||
float4 iy = Pi.yyww;
|
||||
float4 fx = Pf.xzxz;
|
||||
float4 fy = Pf.yyww;
|
||||
|
||||
float4 i = permute(permute(ix) + iy);
|
||||
|
||||
float4 gx = frac(i / 41.0) * 2.0 - 1.0 ;
|
||||
float4 gy = abs(gx) - 0.5 ;
|
||||
float4 tx = floor(gx + 0.5);
|
||||
gx = gx - tx;
|
||||
|
||||
float2 g00 = float2(gx.x,gy.x);
|
||||
float2 g10 = float2(gx.y,gy.y);
|
||||
float2 g01 = float2(gx.z,gy.z);
|
||||
float2 g11 = float2(gx.w,gy.w);
|
||||
|
||||
float4 norm = taylorInvSqrt(float4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11)));
|
||||
g00 *= norm.x;
|
||||
g01 *= norm.y;
|
||||
g10 *= norm.z;
|
||||
g11 *= norm.w;
|
||||
|
||||
float n00 = dot(g00, float2(fx.x, fy.x));
|
||||
float n10 = dot(g10, float2(fx.y, fy.y));
|
||||
float n01 = dot(g01, float2(fx.z, fy.z));
|
||||
float n11 = dot(g11, float2(fx.w, fy.w));
|
||||
|
||||
float2 fade_xy = fade(Pf.xy);
|
||||
float2 n_x = lerp(float2(n00, n01), float2(n10, n11), fade_xy.x);
|
||||
float n_xy = lerp(n_x.x, n_x.y, fade_xy.y);
|
||||
return 2.3 * n_xy;
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e1b0b5fd6a4e44923b40d856017562e1
|
||||
ShaderImporter:
|
||||
defaultTextures: []
|
||||
userData:
|
||||
|
|
@ -1,194 +0,0 @@
|
|||
//
|
||||
// Noise Shader Library for Unity - https://github.com/keijiro/NoiseShader
|
||||
//
|
||||
// Original work (webgl-noise) Copyright (C) 2011 Stefan Gustavson
|
||||
// Translation and modification was made by Keijiro Takahashi.
|
||||
//
|
||||
// This shader is based on the webgl-noise GLSL shader. For further details
|
||||
// of the original shader, please see the following description from the
|
||||
// original source code.
|
||||
//
|
||||
|
||||
//
|
||||
// GLSL textureless classic 3D noise "cnoise",
|
||||
// with an RSL-style periodic variant "pnoise".
|
||||
// Author: Stefan Gustavson (stefan.gustavson@liu.se)
|
||||
// Version: 2011-10-11
|
||||
//
|
||||
// Many thanks to Ian McEwan of Ashima Arts for the
|
||||
// ideas for permutation and gradient selection.
|
||||
//
|
||||
// Copyright (c) 2011 Stefan Gustavson. All rights reserved.
|
||||
// Distributed under the MIT license. See LICENSE file.
|
||||
// https://github.com/ashima/webgl-noise
|
||||
//
|
||||
|
||||
float3 mod(float3 x, float3 y)
|
||||
{
|
||||
return x - y * floor(x / y);
|
||||
}
|
||||
|
||||
float3 mod289(float3 x)
|
||||
{
|
||||
return x - floor(x / 289.0) * 289.0;
|
||||
}
|
||||
|
||||
float4 mod289(float4 x)
|
||||
{
|
||||
return x - floor(x / 289.0) * 289.0;
|
||||
}
|
||||
|
||||
float4 permute(float4 x)
|
||||
{
|
||||
return mod289(((x*34.0)+1.0)*x);
|
||||
}
|
||||
|
||||
float4 taylorInvSqrt(float4 r)
|
||||
{
|
||||
return (float4)1.79284291400159 - r * 0.85373472095314;
|
||||
}
|
||||
|
||||
float3 fade(float3 t) {
|
||||
return t*t*t*(t*(t*6.0-15.0)+10.0);
|
||||
}
|
||||
|
||||
// Classic Perlin noise
|
||||
float cnoise(float3 P)
|
||||
{
|
||||
float3 Pi0 = floor(P); // Integer part for indexing
|
||||
float3 Pi1 = Pi0 + (float3)1.0; // Integer part + 1
|
||||
Pi0 = mod289(Pi0);
|
||||
Pi1 = mod289(Pi1);
|
||||
float3 Pf0 = frac(P); // Fractional part for interpolation
|
||||
float3 Pf1 = Pf0 - (float3)1.0; // Fractional part - 1.0
|
||||
float4 ix = float4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);
|
||||
float4 iy = float4(Pi0.y, Pi0.y, Pi1.y, Pi1.y);
|
||||
float4 iz0 = (float4)Pi0.z;
|
||||
float4 iz1 = (float4)Pi1.z;
|
||||
|
||||
float4 ixy = permute(permute(ix) + iy);
|
||||
float4 ixy0 = permute(ixy + iz0);
|
||||
float4 ixy1 = permute(ixy + iz1);
|
||||
|
||||
float4 gx0 = ixy0 / 7.0;
|
||||
float4 gy0 = frac(floor(gx0) / 7.0) - 0.5;
|
||||
gx0 = frac(gx0);
|
||||
float4 gz0 = (float4)0.5 - abs(gx0) - abs(gy0);
|
||||
float4 sz0 = step(gz0, (float4)0.0);
|
||||
gx0 -= sz0 * (step((float4)0.0, gx0) - 0.5);
|
||||
gy0 -= sz0 * (step((float4)0.0, gy0) - 0.5);
|
||||
|
||||
float4 gx1 = ixy1 / 7.0;
|
||||
float4 gy1 = frac(floor(gx1) / 7.0) - 0.5;
|
||||
gx1 = frac(gx1);
|
||||
float4 gz1 = (float4)0.5 - abs(gx1) - abs(gy1);
|
||||
float4 sz1 = step(gz1, (float4)0.0);
|
||||
gx1 -= sz1 * (step((float4)0.0, gx1) - 0.5);
|
||||
gy1 -= sz1 * (step((float4)0.0, gy1) - 0.5);
|
||||
|
||||
float3 g000 = float3(gx0.x,gy0.x,gz0.x);
|
||||
float3 g100 = float3(gx0.y,gy0.y,gz0.y);
|
||||
float3 g010 = float3(gx0.z,gy0.z,gz0.z);
|
||||
float3 g110 = float3(gx0.w,gy0.w,gz0.w);
|
||||
float3 g001 = float3(gx1.x,gy1.x,gz1.x);
|
||||
float3 g101 = float3(gx1.y,gy1.y,gz1.y);
|
||||
float3 g011 = float3(gx1.z,gy1.z,gz1.z);
|
||||
float3 g111 = float3(gx1.w,gy1.w,gz1.w);
|
||||
|
||||
float4 norm0 = taylorInvSqrt(float4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110)));
|
||||
g000 *= norm0.x;
|
||||
g010 *= norm0.y;
|
||||
g100 *= norm0.z;
|
||||
g110 *= norm0.w;
|
||||
|
||||
float4 norm1 = taylorInvSqrt(float4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111)));
|
||||
g001 *= norm1.x;
|
||||
g011 *= norm1.y;
|
||||
g101 *= norm1.z;
|
||||
g111 *= norm1.w;
|
||||
|
||||
float n000 = dot(g000, Pf0);
|
||||
float n100 = dot(g100, float3(Pf1.x, Pf0.y, Pf0.z));
|
||||
float n010 = dot(g010, float3(Pf0.x, Pf1.y, Pf0.z));
|
||||
float n110 = dot(g110, float3(Pf1.x, Pf1.y, Pf0.z));
|
||||
float n001 = dot(g001, float3(Pf0.x, Pf0.y, Pf1.z));
|
||||
float n101 = dot(g101, float3(Pf1.x, Pf0.y, Pf1.z));
|
||||
float n011 = dot(g011, float3(Pf0.x, Pf1.y, Pf1.z));
|
||||
float n111 = dot(g111, Pf1);
|
||||
|
||||
float3 fade_xyz = fade(Pf0);
|
||||
float4 n_z = lerp(float4(n000, n100, n010, n110), float4(n001, n101, n011, n111), fade_xyz.z);
|
||||
float2 n_yz = lerp(n_z.xy, n_z.zw, fade_xyz.y);
|
||||
float n_xyz = lerp(n_yz.x, n_yz.y, fade_xyz.x);
|
||||
return 2.2 * n_xyz;
|
||||
}
|
||||
|
||||
// Classic Perlin noise, periodic variant
|
||||
float pnoise(float3 P, float3 rep)
|
||||
{
|
||||
float3 Pi0 = mod(floor(P), rep); // Integer part, modulo period
|
||||
float3 Pi1 = mod(Pi0 + (float3)1.0, rep); // Integer part + 1, mod period
|
||||
Pi0 = mod289(Pi0);
|
||||
Pi1 = mod289(Pi1);
|
||||
float3 Pf0 = frac(P); // Fractional part for interpolation
|
||||
float3 Pf1 = Pf0 - (float3)1.0; // Fractional part - 1.0
|
||||
float4 ix = float4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);
|
||||
float4 iy = float4(Pi0.y, Pi0.y, Pi1.y, Pi1.y);
|
||||
float4 iz0 = (float4)Pi0.z;
|
||||
float4 iz1 = (float4)Pi1.z;
|
||||
|
||||
float4 ixy = permute(permute(ix) + iy);
|
||||
float4 ixy0 = permute(ixy + iz0);
|
||||
float4 ixy1 = permute(ixy + iz1);
|
||||
|
||||
float4 gx0 = ixy0 / 7.0;
|
||||
float4 gy0 = frac(floor(gx0) / 7.0) - 0.5;
|
||||
gx0 = frac(gx0);
|
||||
float4 gz0 = (float4)0.5 - abs(gx0) - abs(gy0);
|
||||
float4 sz0 = step(gz0, (float4)0.0);
|
||||
gx0 -= sz0 * (step((float4)0.0, gx0) - 0.5);
|
||||
gy0 -= sz0 * (step((float4)0.0, gy0) - 0.5);
|
||||
|
||||
float4 gx1 = ixy1 / 7.0;
|
||||
float4 gy1 = frac(floor(gx1) / 7.0) - 0.5;
|
||||
gx1 = frac(gx1);
|
||||
float4 gz1 = (float4)0.5 - abs(gx1) - abs(gy1);
|
||||
float4 sz1 = step(gz1, (float4)0.0);
|
||||
gx1 -= sz1 * (step((float4)0.0, gx1) - 0.5);
|
||||
gy1 -= sz1 * (step((float4)0.0, gy1) - 0.5);
|
||||
|
||||
float3 g000 = float3(gx0.x,gy0.x,gz0.x);
|
||||
float3 g100 = float3(gx0.y,gy0.y,gz0.y);
|
||||
float3 g010 = float3(gx0.z,gy0.z,gz0.z);
|
||||
float3 g110 = float3(gx0.w,gy0.w,gz0.w);
|
||||
float3 g001 = float3(gx1.x,gy1.x,gz1.x);
|
||||
float3 g101 = float3(gx1.y,gy1.y,gz1.y);
|
||||
float3 g011 = float3(gx1.z,gy1.z,gz1.z);
|
||||
float3 g111 = float3(gx1.w,gy1.w,gz1.w);
|
||||
|
||||
float4 norm0 = taylorInvSqrt(float4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110)));
|
||||
g000 *= norm0.x;
|
||||
g010 *= norm0.y;
|
||||
g100 *= norm0.z;
|
||||
g110 *= norm0.w;
|
||||
float4 norm1 = taylorInvSqrt(float4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111)));
|
||||
g001 *= norm1.x;
|
||||
g011 *= norm1.y;
|
||||
g101 *= norm1.z;
|
||||
g111 *= norm1.w;
|
||||
|
||||
float n000 = dot(g000, Pf0);
|
||||
float n100 = dot(g100, float3(Pf1.x, Pf0.y, Pf0.z));
|
||||
float n010 = dot(g010, float3(Pf0.x, Pf1.y, Pf0.z));
|
||||
float n110 = dot(g110, float3(Pf1.x, Pf1.y, Pf0.z));
|
||||
float n001 = dot(g001, float3(Pf0.x, Pf0.y, Pf1.z));
|
||||
float n101 = dot(g101, float3(Pf1.x, Pf0.y, Pf1.z));
|
||||
float n011 = dot(g011, float3(Pf0.x, Pf1.y, Pf1.z));
|
||||
float n111 = dot(g111, Pf1);
|
||||
|
||||
float3 fade_xyz = fade(Pf0);
|
||||
float4 n_z = lerp(float4(n000, n100, n010, n110), float4(n001, n101, n011, n111), fade_xyz.z);
|
||||
float2 n_yz = lerp(n_z.xy, n_z.zw, fade_xyz.y);
|
||||
float n_xyz = lerp(n_yz.x, n_yz.y, fade_xyz.x);
|
||||
return 2.2 * n_xyz;
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3ba1abd1cb44c47b68fbb577c6c4ba3e
|
||||
ShaderImporter:
|
||||
defaultTextures: []
|
||||
userData:
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
//
|
||||
// Noise Shader Library for Unity - https://github.com/keijiro/NoiseShader
|
||||
//
|
||||
// Original work (webgl-noise) Copyright (C) 2011 Ashima Arts.
|
||||
// Translation and modification was made by Keijiro Takahashi.
|
||||
//
|
||||
// This shader is based on the webgl-noise GLSL shader. For further details
|
||||
// of the original shader, please see the following description from the
|
||||
// original source code.
|
||||
//
|
||||
|
||||
//
|
||||
// Description : Array and textureless GLSL 2D simplex noise function.
|
||||
// Author : Ian McEwan, Ashima Arts.
|
||||
// Maintainer : ijm
|
||||
// Lastmod : 20110822 (ijm)
|
||||
// License : Copyright (C) 2011 Ashima Arts. All rights reserved.
|
||||
// Distributed under the MIT License. See LICENSE file.
|
||||
// https://github.com/ashima/webgl-noise
|
||||
//
|
||||
|
||||
float3 mod289(float3 x)
|
||||
{
|
||||
return x - floor(x / 289.0) * 289.0;
|
||||
}
|
||||
|
||||
float2 mod289(float2 x)
|
||||
{
|
||||
return x - floor(x / 289.0) * 289.0;
|
||||
}
|
||||
|
||||
float3 permute(float3 x)
|
||||
{
|
||||
return mod289((x * 34.0 + 1.0) * x);
|
||||
}
|
||||
|
||||
float3 taylorInvSqrt(float3 r)
|
||||
{
|
||||
return 1.79284291400159 - 0.85373472095314 * r;
|
||||
}
|
||||
|
||||
float snoise(float2 v)
|
||||
{
|
||||
const float4 C = float4( 0.211324865405187, // (3.0-sqrt(3.0))/6.0
|
||||
0.366025403784439, // 0.5*(sqrt(3.0)-1.0)
|
||||
-0.577350269189626, // -1.0 + 2.0 * C.x
|
||||
0.024390243902439); // 1.0 / 41.0
|
||||
// First corner
|
||||
float2 i = floor(v + dot(v, C.yy));
|
||||
float2 x0 = v - i + dot(i, C.xx);
|
||||
|
||||
// Other corners
|
||||
float2 i1;
|
||||
i1.x = step(x0.y, x0.x);
|
||||
i1.y = 1.0 - i1.x;
|
||||
|
||||
// x1 = x0 - i1 + 1.0 * C.xx;
|
||||
// x2 = x0 - 1.0 + 2.0 * C.xx;
|
||||
float2 x1 = x0 + C.xx - i1;
|
||||
float2 x2 = x0 + C.zz;
|
||||
|
||||
// Permutations
|
||||
i = mod289(i); // Avoid truncation effects in permutation
|
||||
float3 p =
|
||||
permute(permute(i.y + float3(0.0, i1.y, 1.0))
|
||||
+ i.x + float3(0.0, i1.x, 1.0));
|
||||
|
||||
float3 m = max(0.5 - float3(dot(x0, x0), dot(x1, x1), dot(x2, x2)), 0.0);
|
||||
m = m * m;
|
||||
m = m * m;
|
||||
|
||||
// Gradients: 41 points uniformly over a line, mapped onto a diamond.
|
||||
// The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)
|
||||
float3 x = 2.0 * frac(p * C.www) - 1.0;
|
||||
float3 h = abs(x) - 0.5;
|
||||
float3 ox = floor(x + 0.5);
|
||||
float3 a0 = x - ox;
|
||||
|
||||
// Normalise gradients implicitly by scaling m
|
||||
m *= taylorInvSqrt(a0 * a0 + h * h);
|
||||
|
||||
// Compute final noise value at P
|
||||
float3 g;
|
||||
g.x = a0.x * x0.x + h.x * x0.y;
|
||||
g.y = a0.y * x1.x + h.y * x1.y;
|
||||
g.z = a0.z * x2.x + h.z * x2.y;
|
||||
return 130.0 * dot(m, g);
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0a38e0bb6f84203469f3e9cb8545ffce
|
||||
timeCreated: 1448775185
|
||||
licenseType: Pro
|
||||
ShaderImporter:
|
||||
defaultTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
//
|
||||
// Noise Shader Library for Unity - https://github.com/keijiro/NoiseShader
|
||||
//
|
||||
// Original work (webgl-noise) Copyright (C) 2011 Ashima Arts.
|
||||
// Translation and modification was made by Keijiro Takahashi.
|
||||
//
|
||||
// This shader is based on the webgl-noise GLSL shader. For further details
|
||||
// of the original shader, please see the following description from the
|
||||
// original source code.
|
||||
//
|
||||
|
||||
//
|
||||
// Description : Array and textureless GLSL 2D/3D/4D simplex
|
||||
// noise functions.
|
||||
// Author : Ian McEwan, Ashima Arts.
|
||||
// Maintainer : ijm
|
||||
// Lastmod : 20110822 (ijm)
|
||||
// License : Copyright (C) 2011 Ashima Arts. All rights reserved.
|
||||
// Distributed under the MIT License. See LICENSE file.
|
||||
// https://github.com/ashima/webgl-noise
|
||||
//
|
||||
|
||||
float3 mod289(float3 x)
|
||||
{
|
||||
return x - floor(x / 289.0) * 289.0;
|
||||
}
|
||||
|
||||
float4 mod289(float4 x)
|
||||
{
|
||||
return x - floor(x / 289.0) * 289.0;
|
||||
}
|
||||
|
||||
float4 permute(float4 x)
|
||||
{
|
||||
return mod289((x * 34.0 + 1.0) * x);
|
||||
}
|
||||
|
||||
float4 taylorInvSqrt(float4 r)
|
||||
{
|
||||
return 1.79284291400159 - r * 0.85373472095314;
|
||||
}
|
||||
|
||||
float snoise(float3 v)
|
||||
{
|
||||
const float2 C = float2(1.0 / 6.0, 1.0 / 3.0);
|
||||
|
||||
// First corner
|
||||
float3 i = floor(v + dot(v, C.yyy));
|
||||
float3 x0 = v - i + dot(i, C.xxx);
|
||||
|
||||
// Other corners
|
||||
float3 g = step(x0.yzx, x0.xyz);
|
||||
float3 l = 1.0 - g;
|
||||
float3 i1 = min(g.xyz, l.zxy);
|
||||
float3 i2 = max(g.xyz, l.zxy);
|
||||
|
||||
// x1 = x0 - i1 + 1.0 * C.xxx;
|
||||
// x2 = x0 - i2 + 2.0 * C.xxx;
|
||||
// x3 = x0 - 1.0 + 3.0 * C.xxx;
|
||||
float3 x1 = x0 - i1 + C.xxx;
|
||||
float3 x2 = x0 - i2 + C.yyy;
|
||||
float3 x3 = x0 - 0.5;
|
||||
|
||||
// Permutations
|
||||
i = mod289(i); // Avoid truncation effects in permutation
|
||||
float4 p =
|
||||
permute(permute(permute(i.z + float4(0.0, i1.z, i2.z, 1.0))
|
||||
+ i.y + float4(0.0, i1.y, i2.y, 1.0))
|
||||
+ i.x + float4(0.0, i1.x, i2.x, 1.0));
|
||||
|
||||
// Gradients: 7x7 points over a square, mapped onto an octahedron.
|
||||
// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
|
||||
float4 j = p - 49.0 * floor(p / 49.0); // mod(p,7*7)
|
||||
|
||||
float4 x_ = floor(j / 7.0);
|
||||
float4 y_ = floor(j - 7.0 * x_); // mod(j,N)
|
||||
|
||||
float4 x = (x_ * 2.0 + 0.5) / 7.0 - 1.0;
|
||||
float4 y = (y_ * 2.0 + 0.5) / 7.0 - 1.0;
|
||||
|
||||
float4 h = 1.0 - abs(x) - abs(y);
|
||||
|
||||
float4 b0 = float4(x.xy, y.xy);
|
||||
float4 b1 = float4(x.zw, y.zw);
|
||||
|
||||
//float4 s0 = float4(lessThan(b0, 0.0)) * 2.0 - 1.0;
|
||||
//float4 s1 = float4(lessThan(b1, 0.0)) * 2.0 - 1.0;
|
||||
float4 s0 = floor(b0) * 2.0 + 1.0;
|
||||
float4 s1 = floor(b1) * 2.0 + 1.0;
|
||||
float4 sh = -step(h, 0.0);
|
||||
|
||||
float4 a0 = b0.xzyw + s0.xzyw * sh.xxyy;
|
||||
float4 a1 = b1.xzyw + s1.xzyw * sh.zzww;
|
||||
|
||||
float3 g0 = float3(a0.xy, h.x);
|
||||
float3 g1 = float3(a0.zw, h.y);
|
||||
float3 g2 = float3(a1.xy, h.z);
|
||||
float3 g3 = float3(a1.zw, h.w);
|
||||
|
||||
// Normalise gradients
|
||||
float4 norm = taylorInvSqrt(float4(dot(g0, g0), dot(g1, g1), dot(g2, g2), dot(g3, g3)));
|
||||
g0 *= norm.x;
|
||||
g1 *= norm.y;
|
||||
g2 *= norm.z;
|
||||
g3 *= norm.w;
|
||||
|
||||
// Mix final noise value
|
||||
float4 m = max(0.6 - float4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0);
|
||||
m = m * m;
|
||||
m = m * m;
|
||||
|
||||
float4 px = float4(dot(x0, g0), dot(x1, g1), dot(x2, g2), dot(x3, g3));
|
||||
return 42.0 * dot(m, px);
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b577a94f58a1398409bfcfe9cd5dbf99
|
||||
timeCreated: 1448788105
|
||||
licenseType: Pro
|
||||
ShaderImporter:
|
||||
defaultTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
//
|
||||
// Noise Shader Library for Unity - https://github.com/keijiro/NoiseShader
|
||||
//
|
||||
// Original work (webgl-noise) Copyright (C) 2011 Ashima Arts.
|
||||
// Translation and modification was made by Keijiro Takahashi.
|
||||
//
|
||||
// This shader is based on the webgl-noise GLSL shader. For further details
|
||||
// of the original shader, please see the following description from the
|
||||
// original source code.
|
||||
//
|
||||
|
||||
//
|
||||
// Description : Array and textureless GLSL 2D simplex noise function.
|
||||
// Author : Ian McEwan, Ashima Arts.
|
||||
// Maintainer : ijm
|
||||
// Lastmod : 20110822 (ijm)
|
||||
// License : Copyright (C) 2011 Ashima Arts. All rights reserved.
|
||||
// Distributed under the MIT License. See LICENSE file.
|
||||
// https://github.com/ashima/webgl-noise
|
||||
//
|
||||
|
||||
#include "SimplexNoise2D.cginc"
|
||||
|
||||
float2 snoise_grad(float2 v)
|
||||
{
|
||||
const float4 C = float4( 0.211324865405187, // (3.0-sqrt(3.0))/6.0
|
||||
0.366025403784439, // 0.5*(sqrt(3.0)-1.0)
|
||||
-0.577350269189626, // -1.0 + 2.0 * C.x
|
||||
0.024390243902439); // 1.0 / 41.0
|
||||
// First corner
|
||||
float2 i = floor(v + dot(v, C.yy));
|
||||
float2 x0 = v - i + dot(i, C.xx);
|
||||
|
||||
// Other corners
|
||||
float2 i1;
|
||||
i1.x = step(x0.y, x0.x);
|
||||
i1.y = 1.0 - i1.x;
|
||||
|
||||
// x1 = x0 - i1 + 1.0 * C.xx;
|
||||
// x2 = x0 - 1.0 + 2.0 * C.xx;
|
||||
float2 x1 = x0 + C.xx - i1;
|
||||
float2 x2 = x0 + C.zz;
|
||||
|
||||
// Permutations
|
||||
i = mod289(i); // Avoid truncation effects in permutation
|
||||
float3 p =
|
||||
permute(permute(i.y + float3(0.0, i1.y, 1.0))
|
||||
+ i.x + float3(0.0, i1.x, 1.0));
|
||||
|
||||
float3 m = max(0.5 - float3(dot(x0, x0), dot(x1, x1), dot(x2, x2)), 0.0);
|
||||
float3 m2 = m * m;
|
||||
float3 m3 = m2 * m;
|
||||
float3 m4 = m2 * m2;
|
||||
|
||||
// Gradients: 41 points uniformly over a line, mapped onto a diamond.
|
||||
// The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)
|
||||
float3 x = 2.0 * frac(p * C.www) - 1.0;
|
||||
float3 h = abs(x) - 0.5;
|
||||
float3 ox = floor(x + 0.5);
|
||||
float3 a0 = x - ox;
|
||||
|
||||
// Normalise gradients
|
||||
float3 norm = taylorInvSqrt(a0 * a0 + h * h);
|
||||
float2 g0 = float2(a0.x, h.x) * norm.x;
|
||||
float2 g1 = float2(a0.y, h.y) * norm.y;
|
||||
float2 g2 = float2(a0.z, h.z) * norm.z;
|
||||
|
||||
// Compute gradient of noise function at P
|
||||
float2 grad =
|
||||
-6.0 * m3.x * x0 * dot(x0, g0) + m4.x * g0 +
|
||||
-6.0 * m3.y * x1 * dot(x1, g1) + m4.y * g1 +
|
||||
-6.0 * m3.z * x2 * dot(x2, g2) + m4.z * g2;
|
||||
return 130.0 * grad;
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 33c8157a2aca37849bc1f21250315a93
|
||||
timeCreated: 1449292284
|
||||
licenseType: Pro
|
||||
ShaderImporter:
|
||||
defaultTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
//
|
||||
// Noise Shader Library for Unity - https://github.com/keijiro/NoiseShader
|
||||
//
|
||||
// Original work (webgl-noise) Copyright (C) 2011 Ashima Arts.
|
||||
// Translation and modification was made by Keijiro Takahashi.
|
||||
//
|
||||
// This shader is based on the webgl-noise GLSL shader. For further details
|
||||
// of the original shader, please see the following description from the
|
||||
// original source code.
|
||||
//
|
||||
|
||||
//
|
||||
// Description : Array and textureless GLSL 2D/3D/4D simplex
|
||||
// noise functions.
|
||||
// Author : Ian McEwan, Ashima Arts.
|
||||
// Maintainer : ijm
|
||||
// Lastmod : 20110822 (ijm)
|
||||
// License : Copyright (C) 2011 Ashima Arts. All rights reserved.
|
||||
// Distributed under the MIT License. See LICENSE file.
|
||||
// https://github.com/ashima/webgl-noise
|
||||
//
|
||||
|
||||
float3 mod289(float3 x)
|
||||
{
|
||||
return x - floor(x / 289.0) * 289.0;
|
||||
}
|
||||
|
||||
float4 mod289(float4 x)
|
||||
{
|
||||
return x - floor(x / 289.0) * 289.0;
|
||||
}
|
||||
|
||||
float4 permute(float4 x)
|
||||
{
|
||||
return mod289((x * 34.0 + 1.0) * x);
|
||||
}
|
||||
|
||||
float4 taylorInvSqrt(float4 r)
|
||||
{
|
||||
return 1.79284291400159 - r * 0.85373472095314;
|
||||
}
|
||||
|
||||
float3 snoise_grad(float3 v)
|
||||
{
|
||||
const float2 C = float2(1.0 / 6.0, 1.0 / 3.0);
|
||||
|
||||
// First corner
|
||||
float3 i = floor(v + dot(v, C.yyy));
|
||||
float3 x0 = v - i + dot(i, C.xxx);
|
||||
|
||||
// Other corners
|
||||
float3 g = step(x0.yzx, x0.xyz);
|
||||
float3 l = 1.0 - g;
|
||||
float3 i1 = min(g.xyz, l.zxy);
|
||||
float3 i2 = max(g.xyz, l.zxy);
|
||||
|
||||
// x1 = x0 - i1 + 1.0 * C.xxx;
|
||||
// x2 = x0 - i2 + 2.0 * C.xxx;
|
||||
// x3 = x0 - 1.0 + 3.0 * C.xxx;
|
||||
float3 x1 = x0 - i1 + C.xxx;
|
||||
float3 x2 = x0 - i2 + C.yyy;
|
||||
float3 x3 = x0 - 0.5;
|
||||
|
||||
// Permutations
|
||||
i = mod289(i); // Avoid truncation effects in permutation
|
||||
float4 p =
|
||||
permute(permute(permute(i.z + float4(0.0, i1.z, i2.z, 1.0))
|
||||
+ i.y + float4(0.0, i1.y, i2.y, 1.0))
|
||||
+ i.x + float4(0.0, i1.x, i2.x, 1.0));
|
||||
|
||||
// Gradients: 7x7 points over a square, mapped onto an octahedron.
|
||||
// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
|
||||
float4 j = p - 49.0 * floor(p / 49.0); // mod(p,7*7)
|
||||
|
||||
float4 x_ = floor(j / 7.0);
|
||||
float4 y_ = floor(j - 7.0 * x_); // mod(j,N)
|
||||
|
||||
float4 x = (x_ * 2.0 + 0.5) / 7.0 - 1.0;
|
||||
float4 y = (y_ * 2.0 + 0.5) / 7.0 - 1.0;
|
||||
|
||||
float4 h = 1.0 - abs(x) - abs(y);
|
||||
|
||||
float4 b0 = float4(x.xy, y.xy);
|
||||
float4 b1 = float4(x.zw, y.zw);
|
||||
|
||||
//float4 s0 = float4(lessThan(b0, 0.0)) * 2.0 - 1.0;
|
||||
//float4 s1 = float4(lessThan(b1, 0.0)) * 2.0 - 1.0;
|
||||
float4 s0 = floor(b0) * 2.0 + 1.0;
|
||||
float4 s1 = floor(b1) * 2.0 + 1.0;
|
||||
float4 sh = -step(h, 0.0);
|
||||
|
||||
float4 a0 = b0.xzyw + s0.xzyw * sh.xxyy;
|
||||
float4 a1 = b1.xzyw + s1.xzyw * sh.zzww;
|
||||
|
||||
float3 g0 = float3(a0.xy, h.x);
|
||||
float3 g1 = float3(a0.zw, h.y);
|
||||
float3 g2 = float3(a1.xy, h.z);
|
||||
float3 g3 = float3(a1.zw, h.w);
|
||||
|
||||
// Normalise gradients
|
||||
float4 norm = taylorInvSqrt(float4(dot(g0, g0), dot(g1, g1), dot(g2, g2), dot(g3, g3)));
|
||||
g0 *= norm.x;
|
||||
g1 *= norm.y;
|
||||
g2 *= norm.z;
|
||||
g3 *= norm.w;
|
||||
|
||||
// Compute gradient of noise function at P
|
||||
float4 m = max(0.6 - float4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0);
|
||||
float4 m2 = m * m;
|
||||
float4 m3 = m2 * m;
|
||||
float4 m4 = m2 * m2;
|
||||
float3 grad =
|
||||
-6.0 * m3.x * x0 * dot(x0, g0) + m4.x * g0 +
|
||||
-6.0 * m3.y * x1 * dot(x1, g1) + m4.y * g1 +
|
||||
-6.0 * m3.z * x2 * dot(x2, g2) + m4.z * g2 +
|
||||
-6.0 * m3.w * x3 * dot(x3, g3) + m4.w * g3;
|
||||
return 42.0 * grad;
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a7111eafc2463224faacf7be3bddb9a4
|
||||
timeCreated: 1449292284
|
||||
licenseType: Pro
|
||||
ShaderImporter:
|
||||
defaultTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4a109a0b0bb6e8f40935ca2ec55e5bd7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using MIConvexHull;
|
||||
|
||||
/// <summary>
|
||||
/// A vertex is a simple class that stores the postion of a point, node or vertex.
|
||||
/// </summary>
|
||||
public class Cell2 : TriangulationCell<Vertex2, Cell2>
|
||||
{
|
||||
Vector2? circumCenter, centroid;
|
||||
|
||||
public Vector2 Circumcenter {
|
||||
get {
|
||||
circumCenter = circumCenter ?? GetCircumcenter();
|
||||
return circumCenter.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2 Centroid {
|
||||
get {
|
||||
centroid = centroid ?? GetCentroid();
|
||||
return centroid.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public Cell2 ()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
double LengthSquared (double[] v)
|
||||
{
|
||||
double norm = 0;
|
||||
for (int i = 0; i < v.Length; i++) {
|
||||
var t = v [i];
|
||||
norm += t * t;
|
||||
}
|
||||
return norm;
|
||||
}
|
||||
|
||||
double Determinant(double[,] m)
|
||||
{
|
||||
double fCofactor00 = m[1,1] * m[2,2] - m[1,2] * m[2,1];
|
||||
double fCofactor10 = m[1,2] * m[2,0] - m[1,0] * m[2,2];
|
||||
double fCofactor20 = m[1,0] * m[2,1] - m[1,1] * m[2,0];
|
||||
|
||||
double fDet = m[0,0] * fCofactor00 + m[0,1] * fCofactor10 + m[0,2] * fCofactor20;
|
||||
|
||||
return fDet;
|
||||
}
|
||||
|
||||
Vector2 GetCircumcenter ()
|
||||
{
|
||||
// From MathWorld: http://mathworld.wolfram.com/Circumcircle.html
|
||||
|
||||
var points = Vertices;
|
||||
|
||||
double[,] m = new double[3, 3];
|
||||
|
||||
// x, y, 1
|
||||
for (int i = 0; i < 3; i++) {
|
||||
m[i, 0] = points[i].x;
|
||||
m[i, 1] = points[i].y;
|
||||
m[i, 2] = 1;
|
||||
}
|
||||
var a = Determinant(m);
|
||||
|
||||
// size, y, 1
|
||||
for (int i = 0; i < 3; i++) {
|
||||
m[i, 0] = points[i].StoredPosition.sqrMagnitude;
|
||||
}
|
||||
var dx = -Determinant(m);
|
||||
|
||||
// size, x, 1
|
||||
for (int i = 0; i < 3; i++) {
|
||||
m[i, 1] = points[i].x;
|
||||
}
|
||||
var dy = Determinant(m);
|
||||
|
||||
// size, x, y
|
||||
//for (int i = 0; i < 3; i++) {
|
||||
// m[i, 2] = points[i].y;
|
||||
//}
|
||||
//var c = -Det(m);
|
||||
|
||||
var s = -1.0 / (2.0 * a);
|
||||
//var r = System.Math.Abs(s) * System.Math.Sqrt(dx * dx + dy * dy - 4 * a * c);
|
||||
|
||||
return new Vector2((float)(s * dx), (float)(s * dy));
|
||||
}
|
||||
|
||||
Vector2 GetCentroid ()
|
||||
{
|
||||
return new Vector2((float)Vertices.Select(v => v.Position[0]).Average(), (float)Vertices.Select(v => v.Position[1]).Average());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: adf902e96e0c51049b65bc002d6ef4ca
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
using System;
|
||||
|
||||
namespace MIConvexHull
|
||||
{
|
||||
internal static class Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// The default plane distance tolerance
|
||||
/// </summary>
|
||||
internal const double DefaultPlaneDistanceTolerance = 1e-10;
|
||||
/// <summary>
|
||||
/// The starting delta dot product in simplex
|
||||
/// </summary>
|
||||
internal const double StartingDeltaDotProductInSimplex = 0.5;
|
||||
/// <summary>
|
||||
/// The connector table size
|
||||
/// </summary>
|
||||
internal const int ConnectorTableSize = 2017;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ee065f025d2f5374db2ceaa973b822d4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,135 +0,0 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MIConvexHull
|
||||
{
|
||||
/// <summary>
|
||||
/// Factory class for computing convex hulls.
|
||||
/// </summary>
|
||||
public static class ConvexHull
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a convex hull of the input data.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex">The type of the t vertex.</typeparam>
|
||||
/// <typeparam name="TFace">The type of the t face.</typeparam>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <param name="PlaneDistanceTolerance">The plane distance tolerance (default is 1e-10). If too high, points
|
||||
/// will be missed. If too low, the algorithm may break. Only adjust if you notice problems.</param>
|
||||
/// <returns>
|
||||
/// ConvexHull<TVertex, TFace>.
|
||||
/// </returns>
|
||||
public static ConvexHull<TVertex, TFace> Create<TVertex, TFace>(IList<TVertex> data,
|
||||
double PlaneDistanceTolerance = Constants.DefaultPlaneDistanceTolerance)
|
||||
where TVertex : IVertex
|
||||
where TFace : ConvexFace<TVertex, TFace>, new()
|
||||
{
|
||||
return ConvexHull<TVertex, TFace>.Create(data, PlaneDistanceTolerance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a convex hull of the input data.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex">The type of the t vertex.</typeparam>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <param name="PlaneDistanceTolerance">The plane distance tolerance (default is 1e-10). If too high, points
|
||||
/// will be missed. If too low, the algorithm may break. Only adjust if you notice problems.</param>
|
||||
/// <returns>
|
||||
/// ConvexHull<TVertex, DefaultConvexFace<TVertex>>.
|
||||
/// </returns>
|
||||
public static ConvexHull<TVertex, DefaultConvexFace<TVertex>> Create<TVertex>(IList<TVertex> data,
|
||||
double PlaneDistanceTolerance = Constants.DefaultPlaneDistanceTolerance)
|
||||
where TVertex : IVertex
|
||||
{
|
||||
return ConvexHull<TVertex, DefaultConvexFace<TVertex>>.Create(data, PlaneDistanceTolerance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a convex hull of the input data.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <param name="PlaneDistanceTolerance">The plane distance tolerance (default is 1e-10). If too high, points
|
||||
/// will be missed. If too low, the algorithm may break. Only adjust if you notice problems.</param>
|
||||
/// <returns>
|
||||
/// ConvexHull<DefaultVertex, DefaultConvexFace<DefaultVertex>>.
|
||||
/// </returns>
|
||||
public static ConvexHull<DefaultVertex, DefaultConvexFace<DefaultVertex>> Create(IList<double[]> data,
|
||||
double PlaneDistanceTolerance = Constants.DefaultPlaneDistanceTolerance)
|
||||
{
|
||||
var points = data.Select(p => new DefaultVertex { Position = p }).ToList();
|
||||
return ConvexHull<DefaultVertex, DefaultConvexFace<DefaultVertex>>.Create(points, PlaneDistanceTolerance);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Representation of a convex hull.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex">The type of the t vertex.</typeparam>
|
||||
/// <typeparam name="TFace">The type of the t face.</typeparam>
|
||||
public class ConvexHull<TVertex, TFace>
|
||||
where TVertex : IVertex
|
||||
where TFace : ConvexFace<TVertex, TFace>, new()
|
||||
{
|
||||
/// <summary>
|
||||
/// Can only be created using a factory method.
|
||||
/// </summary>
|
||||
internal ConvexHull()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Points of the convex hull.
|
||||
/// </summary>
|
||||
/// <value>The points.</value>
|
||||
public IEnumerable<TVertex> Points { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Faces of the convex hull.
|
||||
/// </summary>
|
||||
/// <value>The faces.</value>
|
||||
public IEnumerable<TFace> Faces { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates the convex hull.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <param name="PlaneDistanceTolerance">The plane distance tolerance.</param>
|
||||
/// <returns>
|
||||
/// ConvexHull<TVertex, TFace>.
|
||||
/// </returns>
|
||||
/// <exception cref="System.ArgumentNullException">The supplied data is null.</exception>
|
||||
/// <exception cref="ArgumentNullException">data</exception>
|
||||
public static ConvexHull<TVertex, TFace> Create(IList<TVertex> data, double PlaneDistanceTolerance)
|
||||
{
|
||||
if (data == null) throw new ArgumentNullException("The supplied data is null.");
|
||||
return ConvexHullAlgorithm.GetConvexHull<TVertex, TFace>(data, PlaneDistanceTolerance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 447289e858e19c14abbe91dfbd0e3089
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a6e88469e3b81a541a0bc8a66e60d0c7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,304 +0,0 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
using System;
|
||||
|
||||
namespace MIConvexHull
|
||||
{
|
||||
/// <summary>
|
||||
/// A more lightweight alternative to List of T.
|
||||
/// On clear, only resets the count and does not clear the references
|
||||
/// => this works because of the ObjectManager.
|
||||
/// Includes a stack functionality.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
internal class SimpleList<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// The capacity
|
||||
/// </summary>
|
||||
private int capacity;
|
||||
|
||||
/// <summary>
|
||||
/// The count
|
||||
/// </summary>
|
||||
public int Count;
|
||||
/// <summary>
|
||||
/// The items
|
||||
/// </summary>
|
||||
private T[] items;
|
||||
|
||||
/// <summary>
|
||||
/// Get the i-th element.
|
||||
/// </summary>
|
||||
/// <param name="i">The i.</param>
|
||||
/// <returns>T.</returns>
|
||||
public T this[int i]
|
||||
{
|
||||
get { return items[i]; }
|
||||
set { items[i] = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Size matters.
|
||||
/// </summary>
|
||||
private void EnsureCapacity()
|
||||
{
|
||||
if (capacity == 0)
|
||||
{
|
||||
capacity = 32;
|
||||
items = new T[32];
|
||||
}
|
||||
else
|
||||
{
|
||||
var newItems = new T[capacity*2];
|
||||
Array.Copy(items, newItems, capacity);
|
||||
capacity = 2*capacity;
|
||||
items = newItems;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a vertex to the buffer.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
public void Add(T item)
|
||||
{
|
||||
if (Count + 1 > capacity) EnsureCapacity();
|
||||
items[Count++] = item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes the value to the back of the list.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
public void Push(T item)
|
||||
{
|
||||
if (Count + 1 > capacity) EnsureCapacity();
|
||||
items[Count++] = item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pops the last value from the list.
|
||||
/// </summary>
|
||||
/// <returns>T.</returns>
|
||||
public T Pop()
|
||||
{
|
||||
return items[--Count];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the Count to 0, otherwise does nothing.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
Count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Class IndexBuffer.
|
||||
/// A fancy name for a list of integers.
|
||||
/// </summary>
|
||||
internal class IndexBuffer : SimpleList<int>
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A priority based linked list.
|
||||
/// </summary>
|
||||
internal sealed class FaceList
|
||||
{
|
||||
/// <summary>
|
||||
/// The last
|
||||
/// </summary>
|
||||
private ConvexFaceInternal last;
|
||||
|
||||
/// <summary>
|
||||
/// Get the first element.
|
||||
/// </summary>
|
||||
/// <value>The first.</value>
|
||||
public ConvexFaceInternal First { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds the element to the beginning.
|
||||
/// </summary>
|
||||
/// <param name="face">The face.</param>
|
||||
private void AddFirst(ConvexFaceInternal face)
|
||||
{
|
||||
face.InList = true;
|
||||
First.Previous = face;
|
||||
face.Next = First;
|
||||
First = face;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a face to the list.
|
||||
/// </summary>
|
||||
/// <param name="face">The face.</param>
|
||||
public void Add(ConvexFaceInternal face)
|
||||
{
|
||||
if (face.InList)
|
||||
{
|
||||
if (First.VerticesBeyond.Count < face.VerticesBeyond.Count)
|
||||
{
|
||||
Remove(face);
|
||||
AddFirst(face);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
face.InList = true;
|
||||
|
||||
if (First != null && First.VerticesBeyond.Count < face.VerticesBeyond.Count)
|
||||
{
|
||||
First.Previous = face;
|
||||
face.Next = First;
|
||||
First = face;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (last != null)
|
||||
{
|
||||
last.Next = face;
|
||||
}
|
||||
face.Previous = last;
|
||||
last = face;
|
||||
if (First == null)
|
||||
{
|
||||
First = face;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the element from the list.
|
||||
/// </summary>
|
||||
/// <param name="face">The face.</param>
|
||||
public void Remove(ConvexFaceInternal face)
|
||||
{
|
||||
if (!face.InList) return;
|
||||
|
||||
face.InList = false;
|
||||
|
||||
if (face.Previous != null)
|
||||
{
|
||||
face.Previous.Next = face.Next;
|
||||
}
|
||||
else if ( /*first == face*/ face.Previous == null)
|
||||
{
|
||||
First = face.Next;
|
||||
}
|
||||
|
||||
if (face.Next != null)
|
||||
{
|
||||
face.Next.Previous = face.Previous;
|
||||
}
|
||||
else if ( /*last == face*/ face.Next == null)
|
||||
{
|
||||
last = face.Previous;
|
||||
}
|
||||
|
||||
face.Next = null;
|
||||
face.Previous = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connector list.
|
||||
/// </summary>
|
||||
internal sealed class ConnectorList
|
||||
{
|
||||
/// <summary>
|
||||
/// The last
|
||||
/// </summary>
|
||||
private FaceConnector last;
|
||||
|
||||
/// <summary>
|
||||
/// Get the first element.
|
||||
/// </summary>
|
||||
/// <value>The first.</value>
|
||||
public FaceConnector First { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds the element to the beginning.
|
||||
/// </summary>
|
||||
/// <param name="connector">The connector.</param>
|
||||
private void AddFirst(FaceConnector connector)
|
||||
{
|
||||
First.Previous = connector;
|
||||
connector.Next = First;
|
||||
First = connector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a face to the list.
|
||||
/// </summary>
|
||||
/// <param name="element">The element.</param>
|
||||
public void Add(FaceConnector element)
|
||||
{
|
||||
if (last != null)
|
||||
{
|
||||
last.Next = element;
|
||||
}
|
||||
element.Previous = last;
|
||||
last = element;
|
||||
if (First == null)
|
||||
{
|
||||
First = element;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the element from the list.
|
||||
/// </summary>
|
||||
/// <param name="connector">The connector.</param>
|
||||
public void Remove(FaceConnector connector)
|
||||
{
|
||||
if (connector.Previous != null)
|
||||
{
|
||||
connector.Previous.Next = connector.Next;
|
||||
}
|
||||
else if ( /*first == face*/ connector.Previous == null)
|
||||
{
|
||||
First = connector.Next;
|
||||
}
|
||||
|
||||
if (connector.Next != null)
|
||||
{
|
||||
connector.Next.Previous = connector.Previous;
|
||||
}
|
||||
else if ( /*last == face*/ connector.Next == null)
|
||||
{
|
||||
last = connector.Previous;
|
||||
}
|
||||
|
||||
connector.Next = null;
|
||||
connector.Previous = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3969283304824fe4a8eca1eb522fd3a7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
namespace MIConvexHull
|
||||
{
|
||||
/// <summary>
|
||||
/// A convex face representation containing adjacency information.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex">The type of the t vertex.</typeparam>
|
||||
/// <typeparam name="TFace">The type of the t face.</typeparam>
|
||||
public abstract class ConvexFace<TVertex, TFace>
|
||||
where TVertex : IVertex
|
||||
where TFace : ConvexFace<TVertex, TFace>
|
||||
{
|
||||
/// <summary>
|
||||
/// Adjacency. Array of length "dimension".
|
||||
/// If F = Adjacency[i] then the vertices shared with F are Vertices[j] where j != i.
|
||||
/// In the context of triangulation, can be null (indicates the cell is at boundary).
|
||||
/// </summary>
|
||||
/// <value>The adjacency.</value>
|
||||
public TFace[] Adjacency { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The vertices stored in clockwise order for dimensions 2 - 4, in higher dimensions the order is arbitrary.
|
||||
/// Unless I accidentally switch some index somewhere in which case the order is CCW. Either way, it is consistent.
|
||||
/// 3D Normal = (V[1] - V[0]) x (V[2] - V[1]).
|
||||
/// </summary>
|
||||
/// <value>The vertices.</value>
|
||||
public TVertex[] Vertices { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The normal vector of the face. Null if used in triangulation.
|
||||
/// </summary>
|
||||
/// <value>The normal.</value>
|
||||
public double[] Normal { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A default convex face representation.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex">The type of the t vertex.</typeparam>
|
||||
public class DefaultConvexFace<TVertex> : ConvexFace<TVertex, DefaultConvexFace<TVertex>>
|
||||
where TVertex : IVertex
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5db1a96860ec6d949930695ae9bedcc0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,721 +0,0 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MIConvexHull
|
||||
{
|
||||
/*
|
||||
* This part of the implementation handles initialization of the convex hull algorithm:
|
||||
* - Constructor & Process initiation
|
||||
* - Determine the dimension by looking at length of Position vector of 10 random data points from the input.
|
||||
* - Identify bounding box points in each direction.
|
||||
* - Pick (Dimension + 1) points from the extremes and construct the initial simplex.
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Class ConvexHullAlgorithm.
|
||||
/// </summary>
|
||||
internal partial class ConvexHullAlgorithm
|
||||
{
|
||||
#region Starting functions and constructor
|
||||
/// <summary>
|
||||
/// The main function for the Convex Hull algorithm. It is static, but it creates
|
||||
/// an instantiation of this class in order to allow for parallel execution.
|
||||
/// Following this simple function, the constructor and the main function "FindConvexHull" is listed.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex">The type of the vertices in the data.</typeparam>
|
||||
/// <typeparam name="TFace">The desired type of the faces.</typeparam>
|
||||
/// <param name="data">The data is the vertices as a collection of IVertices.</param>
|
||||
/// <param name="PlaneDistanceTolerance">The plane distance tolerance.</param>
|
||||
/// <returns>
|
||||
/// MIConvexHull.ConvexHull<TVertex, TFace>.
|
||||
/// </returns>
|
||||
internal static ConvexHull<TVertex, TFace> GetConvexHull<TVertex, TFace>(IList<TVertex> data,
|
||||
double PlaneDistanceTolerance)
|
||||
where TFace : ConvexFace<TVertex, TFace>, new()
|
||||
where TVertex : IVertex
|
||||
{
|
||||
var ch = new ConvexHullAlgorithm(data.Cast<IVertex>().ToArray(), false, PlaneDistanceTolerance);
|
||||
ch.GetConvexHull();
|
||||
|
||||
if (ch.NumOfDimensions == 2) return ch.Return2DResultInOrder<TVertex, TFace>(data);
|
||||
return new ConvexHull<TVertex, TFace>
|
||||
{
|
||||
Points = ch.GetHullVertices(data),
|
||||
Faces = ch.GetConvexFaces<TVertex, TFace>()
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConvexHullAlgorithm" /> class.
|
||||
/// </summary>
|
||||
/// <param name="vertices">The vertices.</param>
|
||||
/// <param name="lift">if set to <c>true</c> [lift].</param>
|
||||
/// <param name="PlaneDistanceTolerance">The plane distance tolerance.</param>
|
||||
/// <exception cref="System.InvalidOperationException">Dimension of the input must be 2 or greater.</exception>
|
||||
/// <exception cref="System.ArgumentException">There are too few vertices (m) for the n-dimensional space. (m must be greater +
|
||||
/// than the n, but m is + NumberOfVertices + and n is + NumOfDimensions</exception>
|
||||
/// <exception cref="InvalidOperationException">PointTranslationGenerator cannot be null if PointTranslationType is enabled.
|
||||
/// or
|
||||
/// Dimension of the input must be 2 or greater.</exception>
|
||||
/// <exception cref="ArgumentException">There are too few vertices (m) for the n-dimensional space. (m must be greater " +
|
||||
/// "than the n, but m is " + NumberOfVertices + " and n is " + Dimension</exception>
|
||||
private ConvexHullAlgorithm(IVertex[] vertices, bool lift, double PlaneDistanceTolerance)
|
||||
{
|
||||
IsLifted = lift;
|
||||
Vertices = vertices;
|
||||
NumberOfVertices = vertices.Length;
|
||||
|
||||
NumOfDimensions = DetermineDimension();
|
||||
if (IsLifted) NumOfDimensions++;
|
||||
if (NumOfDimensions < 2) throw new InvalidOperationException("Dimension of the input must be 2 or greater.");
|
||||
if (NumberOfVertices <= NumOfDimensions)
|
||||
throw new ArgumentException(
|
||||
"There are too few vertices (m) for the n-dimensional space. (m must be greater " +
|
||||
"than the n, but m is " + NumberOfVertices + " and n is " + NumOfDimensions);
|
||||
this.PlaneDistanceTolerance = PlaneDistanceTolerance;
|
||||
UnprocessedFaces = new FaceList();
|
||||
ConvexFaces = new IndexBuffer();
|
||||
|
||||
FacePool = new ConvexFaceInternal[(NumOfDimensions + 1) * 10]; // must be initialized before object manager
|
||||
AffectedFaceFlags = new bool[(NumOfDimensions + 1) * 10];
|
||||
ObjectManager = new ObjectManager(this);
|
||||
|
||||
Center = new double[NumOfDimensions];
|
||||
TraverseStack = new IndexBuffer();
|
||||
UpdateBuffer = new int[NumOfDimensions];
|
||||
UpdateIndices = new int[NumOfDimensions];
|
||||
EmptyBuffer = new IndexBuffer();
|
||||
AffectedFaceBuffer = new IndexBuffer();
|
||||
ConeFaceBuffer = new SimpleList<DeferredFace>();
|
||||
SingularVertices = new HashSet<int>();
|
||||
BeyondBuffer = new IndexBuffer();
|
||||
|
||||
ConnectorTable = new ConnectorList[Constants.ConnectorTableSize];
|
||||
for (var i = 0; i < Constants.ConnectorTableSize; i++) ConnectorTable[i] = new ConnectorList();
|
||||
|
||||
VertexVisited = new bool[NumberOfVertices];
|
||||
Positions = new double[NumberOfVertices * NumOfDimensions];
|
||||
boundingBoxPoints = new List<int>[NumOfDimensions];
|
||||
minima = new double[NumOfDimensions];
|
||||
maxima = new double[NumOfDimensions];
|
||||
mathHelper = new MathHelper(NumOfDimensions, Positions);
|
||||
}
|
||||
/// <summary>
|
||||
/// Check the dimensionality of the input data.
|
||||
/// </summary>
|
||||
/// <returns>System.Int32.</returns>
|
||||
/// <exception cref="ArgumentException">Invalid input data (non-uniform dimension).</exception>
|
||||
private int DetermineDimension()
|
||||
{
|
||||
var r = new Random();
|
||||
var dimensions = new List<int>();
|
||||
for (var i = 0; i < 10; i++)
|
||||
dimensions.Add(Vertices[r.Next(NumberOfVertices)].Position.Length);
|
||||
var dimension = dimensions.Min();
|
||||
if (dimension != dimensions.Max())
|
||||
throw new ArgumentException("Invalid input data (non-uniform dimension).");
|
||||
return dimension;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets/calculates the convex hull. This is
|
||||
/// </summary>
|
||||
private void GetConvexHull()
|
||||
{
|
||||
// accessing a 1D array is quicker than a jagged array, so the first step is to make this array
|
||||
SerializeVerticesToPositions();
|
||||
// next the bounding box extremes are found. This is used to shift, scale and find the starting simplex.
|
||||
FindBoundingBoxPoints();
|
||||
// the positions are shifted to avoid divide by zero problems
|
||||
// and if Delaunay or Voronoi, then the parabola terms are scaled back to match the size of the other coords
|
||||
ShiftAndScalePositions();
|
||||
// Find the (dimension+1) initial points and create the simplexes.
|
||||
CreateInitialSimplex();
|
||||
|
||||
// Now, the main loop. These initial faces of a simplex are replaced and expanded
|
||||
// outwards to make the convex hull and faces.
|
||||
while (UnprocessedFaces.First != null)
|
||||
{
|
||||
var currentFace = UnprocessedFaces.First;
|
||||
CurrentVertex = currentFace.FurthestVertex;
|
||||
|
||||
UpdateCenter();
|
||||
|
||||
// The affected faces get tagged
|
||||
TagAffectedFaces(currentFace);
|
||||
|
||||
// Create the cone from the currentVertex and the affected faces horizon.
|
||||
if (!SingularVertices.Contains(CurrentVertex) && CreateCone()) CommitCone();
|
||||
else HandleSingular();
|
||||
|
||||
// Need to reset the tags
|
||||
var count = AffectedFaceBuffer.Count;
|
||||
for (var i = 0; i < count; i++) AffectedFaceFlags[AffectedFaceBuffer[i]] = false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the vertices into the 1D array, Positions. The 1D array has much quicker access in C#.
|
||||
/// </summary>
|
||||
private void SerializeVerticesToPositions()
|
||||
{
|
||||
var index = 0;
|
||||
if (IsLifted) // "Lifted" means that the last dimension is the sum of the squares of the others.
|
||||
{
|
||||
foreach (var v in Vertices)
|
||||
{
|
||||
var parabolaTerm = 0.0; // the lifted term is a sum of squares.
|
||||
var origNumDim = NumOfDimensions - 1;
|
||||
for (var i = 0; i < origNumDim; i++)
|
||||
{
|
||||
var coordinate = v.Position[i];
|
||||
Positions[index++] = coordinate;
|
||||
parabolaTerm += coordinate * coordinate;
|
||||
}
|
||||
Positions[index++] = parabolaTerm;
|
||||
}
|
||||
}
|
||||
else
|
||||
foreach (var v in Vertices)
|
||||
{
|
||||
for (var i = 0; i < NumOfDimensions; i++)
|
||||
Positions[index++] = v.Position[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Finds the bounding box points.
|
||||
/// </summary>
|
||||
private void FindBoundingBoxPoints()
|
||||
{
|
||||
indexOfDimensionWithLeastExtremes = -1;
|
||||
var minNumExtremes = int.MaxValue;
|
||||
for (var i = 0; i < NumOfDimensions; i++)
|
||||
{
|
||||
var minIndices = new List<int>();
|
||||
var maxIndices = new List<int>();
|
||||
double min = double.PositiveInfinity, max = double.NegativeInfinity;
|
||||
for (var j = 0; j < NumberOfVertices; j++)
|
||||
{
|
||||
var v = GetCoordinate(j, i);
|
||||
var difference = min - v;
|
||||
if (difference >= PlaneDistanceTolerance)
|
||||
{
|
||||
// you found a better solution than before, clear out the list and store new value
|
||||
min = v;
|
||||
minIndices.Clear();
|
||||
minIndices.Add(j);
|
||||
}
|
||||
else if (difference > 0)
|
||||
{
|
||||
// you found a solution slightly better than before, clear out those that are no longer on the list and store new value
|
||||
min = v;
|
||||
minIndices.RemoveAll(index => min - GetCoordinate(index, i) > PlaneDistanceTolerance);
|
||||
minIndices.Add(j);
|
||||
}
|
||||
else if (difference > -PlaneDistanceTolerance)
|
||||
{
|
||||
//same or almost as good as current limit, so store it
|
||||
minIndices.Add(j);
|
||||
}
|
||||
difference = v - max;
|
||||
if (difference >= PlaneDistanceTolerance)
|
||||
{
|
||||
// you found a better solution than before, clear out the list and store new value
|
||||
max = v;
|
||||
maxIndices.Clear();
|
||||
maxIndices.Add(j);
|
||||
}
|
||||
else if (difference > 0)
|
||||
{
|
||||
// you found a solution slightly better than before, clear out those that are no longer on the list and store new value
|
||||
max = v;
|
||||
maxIndices.RemoveAll(index => min - GetCoordinate(index, i) > PlaneDistanceTolerance);
|
||||
maxIndices.Add(j);
|
||||
}
|
||||
else if (difference > -PlaneDistanceTolerance)
|
||||
{
|
||||
//same or almost as good as current limit, so store it
|
||||
maxIndices.Add(j);
|
||||
}
|
||||
}
|
||||
minima[i] = min;
|
||||
maxima[i] = max;
|
||||
minIndices.AddRange(maxIndices);
|
||||
if (minIndices.Count < minNumExtremes)
|
||||
{
|
||||
minNumExtremes = minIndices.Count;
|
||||
indexOfDimensionWithLeastExtremes = i;
|
||||
}
|
||||
boundingBoxPoints[i] = minIndices;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shifts and scales the Positions to avoid future errors. This does not alter the original data.
|
||||
/// </summary>
|
||||
private void ShiftAndScalePositions()
|
||||
{
|
||||
var positionsLength = Positions.Length;
|
||||
if (IsLifted)
|
||||
{
|
||||
var origNumDim = NumOfDimensions - 1;
|
||||
var parabolaScale = 2 / (minima.Sum(x => Math.Abs(x)) + maxima.Sum(x => Math.Abs(x))
|
||||
- Math.Abs(maxima[origNumDim]) - Math.Abs(minima[origNumDim]));
|
||||
// the parabola scale is 1 / average of the sum of the other dimensions.
|
||||
// multiplying this by the parabola will scale it back to be on near similar size to the
|
||||
// other dimensions. Without this, the term is much larger than the others, which causes
|
||||
// problems for roundoff error and finding the normal of faces.
|
||||
minima[origNumDim] *= parabolaScale; // change the extreme values as well
|
||||
maxima[origNumDim] *= parabolaScale;
|
||||
// it is done here because
|
||||
for (int i = origNumDim; i < positionsLength; i += NumOfDimensions)
|
||||
Positions[i] *= parabolaScale;
|
||||
}
|
||||
var shiftAmount = new double[NumOfDimensions];
|
||||
for (int i = 0; i < NumOfDimensions; i++)
|
||||
// now the entire model is shifted to all positive numbers...plus some more.
|
||||
// why?
|
||||
// 1) to avoid dealing with a point at the origin {0,0,...,0} which causes problems
|
||||
// for future normal finding
|
||||
// 2) note that weird shift that is used (max - min - min). This is to avoid scaling
|
||||
// issues. this shift means that the minima in a dimension will always be a positive
|
||||
// number (no points at zero), and the minima [in a given dimension] will always be
|
||||
// half of the maxima. 'Half' is much preferred to 'thousands of times'
|
||||
// Think of the first term as the range (max - min), then the second term avoids cases
|
||||
// where there are both positive and negative numbers.
|
||||
if (maxima[i] == minima[i]) shiftAmount[i] = 0.0;
|
||||
else shiftAmount[i] = (maxima[i] - minima[i]) - minima[i];
|
||||
for (int i = 0; i < positionsLength; i++)
|
||||
Positions[i] += shiftAmount[i % NumOfDimensions];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the (dimension+1) initial points and create the simplexes.
|
||||
/// Creates the initial simplex of n+1 vertices by using points from the bounding box.
|
||||
/// Special care is taken to ensure that the vertices chosen do not result in a degenerate shape
|
||||
/// where vertices are collinear (co-planar, etc). This would technically be resolved when additional
|
||||
/// vertices are checked in the main loop, but: 1) a degenerate simplex would not eliminate any other
|
||||
/// vertices (thus no savings there), 2) the creation of the face normal is prone to error.
|
||||
/// </summary>
|
||||
private void CreateInitialSimplex()
|
||||
{
|
||||
var initialPoints = FindInitialPoints();
|
||||
#region Create the first faces from (dimension + 1) vertices.
|
||||
|
||||
var faces = new int[NumOfDimensions + 1];
|
||||
|
||||
for (var i = 0; i < NumOfDimensions + 1; i++)
|
||||
{
|
||||
var vertices = new int[NumOfDimensions];
|
||||
for (int j = 0, k = 0; j <= NumOfDimensions; j++)
|
||||
{
|
||||
if (i != j) vertices[k++] = initialPoints[j];
|
||||
}
|
||||
var newFace = FacePool[ObjectManager.GetFace()];
|
||||
newFace.Vertices = vertices;
|
||||
Array.Sort(vertices);
|
||||
mathHelper.CalculateFacePlane(newFace, Center);
|
||||
faces[i] = newFace.Index;
|
||||
}
|
||||
// update the adjacency (check all pairs of faces)
|
||||
for (var i = 0; i < NumOfDimensions; i++)
|
||||
for (var j = i + 1; j < NumOfDimensions + 1; j++) UpdateAdjacency(FacePool[faces[i]], FacePool[faces[j]]);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Init the vertex beyond buffers.
|
||||
|
||||
foreach (var faceIndex in faces)
|
||||
{
|
||||
var face = FacePool[faceIndex];
|
||||
FindBeyondVertices(face);
|
||||
if (face.VerticesBeyond.Count == 0) ConvexFaces.Add(face.Index); // The face is on the hull
|
||||
else UnprocessedFaces.Add(face);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// Set all vertices to false (unvisited).
|
||||
foreach (var vertex in initialPoints) VertexVisited[vertex] = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds (dimension + 1) initial points.
|
||||
/// </summary>
|
||||
/// <returns>List<System.Int32>.</returns>
|
||||
/// <exception cref="System.ArgumentException">The input data is degenerate. It appears to exist in " + NumOfDimensions +
|
||||
/// " dimensions, but it is a " + (NumOfDimensions - 1) + " dimensional set (i.e. the point of collinear,"
|
||||
/// + " coplanar, or co-hyperplanar.)</exception>
|
||||
private List<int> FindInitialPoints()
|
||||
{
|
||||
var bigNumber = maxima.Sum() * NumOfDimensions * NumberOfVertices;
|
||||
// the first two points are taken from the dimension that had the fewest extremes
|
||||
// well, in most cases there will only be 2 in all dimensions: one min and one max
|
||||
// but a lot of engineering part shapes are nice and square and can have hundreds of
|
||||
// parallel vertices at the extremes
|
||||
var vertex1 = boundingBoxPoints[indexOfDimensionWithLeastExtremes].First(); // these are min and max vertices along
|
||||
var vertex2 = boundingBoxPoints[indexOfDimensionWithLeastExtremes].Last(); // the dimension that had the fewest points
|
||||
boundingBoxPoints[indexOfDimensionWithLeastExtremes].RemoveAt(0);
|
||||
boundingBoxPoints[indexOfDimensionWithLeastExtremes].RemoveAt(boundingBoxPoints[indexOfDimensionWithLeastExtremes].Count - 1);
|
||||
var initialPoints = new List<int> { vertex1, vertex2 };
|
||||
VertexVisited[vertex1] = VertexVisited[vertex2] = true;
|
||||
CurrentVertex = vertex1; UpdateCenter();
|
||||
CurrentVertex = vertex2; UpdateCenter();
|
||||
var edgeVectors = new double[NumOfDimensions][];
|
||||
edgeVectors[0] = mathHelper.VectorBetweenVertices(vertex2, vertex1);
|
||||
// now the remaining vertices are just combined in one big list
|
||||
var extremes = boundingBoxPoints.SelectMany(x => x).ToList();
|
||||
// otherwise find the remaining points by maximizing the initial simplex volume
|
||||
var index = 1;
|
||||
while (index < NumOfDimensions && extremes.Any())
|
||||
{
|
||||
var bestVertex = -1;
|
||||
var bestEdgeVector = new double[] { };
|
||||
var maxVolume = Constants.DefaultPlaneDistanceTolerance;
|
||||
for (var i = extremes.Count - 1; i >= 0; i--)
|
||||
{
|
||||
// count backwards in order to remove potential duplicates
|
||||
var vIndex = extremes[i];
|
||||
if (initialPoints.Contains(vIndex)) extremes.RemoveAt(i);
|
||||
else
|
||||
{
|
||||
edgeVectors[index] = mathHelper.VectorBetweenVertices(vIndex, vertex1);
|
||||
var volume = mathHelper.GetSimplexVolume(edgeVectors, index, bigNumber);
|
||||
if (maxVolume < volume)
|
||||
{
|
||||
maxVolume = volume;
|
||||
bestVertex = vIndex;
|
||||
bestEdgeVector = edgeVectors[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
extremes.Remove(bestVertex);
|
||||
if (bestVertex == -1) break;
|
||||
initialPoints.Add(bestVertex);
|
||||
edgeVectors[index++] = bestEdgeVector;
|
||||
CurrentVertex = bestVertex; UpdateCenter();
|
||||
}
|
||||
// hmm, there are not enough points on the bounding box to make a simplex. It is rare but entirely possibly.
|
||||
// As an extreme, the bounding box can be made in n dimensions from only 2 unique points. When we can't find
|
||||
// enough unique points, we start again with ALL the vertices. The following is a near replica of the code
|
||||
// above, but instead of extremes, we consider "allVertices".
|
||||
if (initialPoints.Count <= NumOfDimensions && !IsLifted)
|
||||
{
|
||||
var allVertices = Enumerable.Range(0, NumberOfVertices).ToList();
|
||||
while (index < NumOfDimensions && allVertices.Any())
|
||||
{
|
||||
var bestVertex = -1;
|
||||
var bestEdgeVector = new double[] { };
|
||||
var maxVolume = 0.0;
|
||||
for (var i = allVertices.Count - 1; i >= 0; i--)
|
||||
{
|
||||
// count backwards in order to remove potential duplicates
|
||||
var vIndex = allVertices[i];
|
||||
if (initialPoints.Contains(vIndex)) allVertices.RemoveAt(i);
|
||||
else
|
||||
{
|
||||
edgeVectors[index] = mathHelper.VectorBetweenVertices(vIndex, vertex1);
|
||||
var volume = mathHelper.GetSimplexVolume(edgeVectors, index, bigNumber);
|
||||
if (maxVolume < volume)
|
||||
{
|
||||
maxVolume = volume;
|
||||
bestVertex = vIndex;
|
||||
bestEdgeVector = edgeVectors[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
allVertices.Remove(bestVertex);
|
||||
if (bestVertex == -1) break;
|
||||
initialPoints.Add(bestVertex);
|
||||
edgeVectors[index++] = bestEdgeVector;
|
||||
CurrentVertex = bestVertex; UpdateCenter();
|
||||
}
|
||||
}
|
||||
if (initialPoints.Count <= NumOfDimensions && IsLifted)
|
||||
{
|
||||
var allVertices = Enumerable.Range(0, NumberOfVertices).ToList();
|
||||
while (index < NumOfDimensions && allVertices.Any())
|
||||
{
|
||||
var bestVertex = -1;
|
||||
var bestEdgeVector = new double[] { };
|
||||
var maxVolume = 0.0;
|
||||
for (var i = allVertices.Count - 1; i >= 0; i--)
|
||||
{
|
||||
// count backwards in order to remove potential duplicates
|
||||
var vIndex = allVertices[i];
|
||||
if (initialPoints.Contains(vIndex)) allVertices.RemoveAt(i);
|
||||
else
|
||||
{
|
||||
mathHelper.RandomOffsetToLift(vIndex, maxima.Last()-minima.Last());
|
||||
edgeVectors[index] = mathHelper.VectorBetweenVertices(vIndex, vertex1);
|
||||
var volume = mathHelper.GetSimplexVolume(edgeVectors, index, bigNumber);
|
||||
if (maxVolume < volume)
|
||||
{
|
||||
maxVolume = volume;
|
||||
bestVertex = vIndex;
|
||||
bestEdgeVector = edgeVectors[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
allVertices.Remove(bestVertex);
|
||||
if (bestVertex == -1) break;
|
||||
initialPoints.Add(bestVertex);
|
||||
edgeVectors[index++] = bestEdgeVector;
|
||||
CurrentVertex = bestVertex; UpdateCenter();
|
||||
}
|
||||
}
|
||||
if (initialPoints.Count <= NumOfDimensions && IsLifted)
|
||||
throw new ArgumentException("The input data is degenerate. It appears to exist in " + NumOfDimensions +
|
||||
" dimensions, but it is a " + (NumOfDimensions - 1) + " dimensional set (i.e. the point of collinear,"
|
||||
+ " coplanar, or co-hyperplanar.)");
|
||||
return initialPoints;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if 2 faces are adjacent and if so, update their AdjacentFaces array.
|
||||
/// </summary>
|
||||
/// <param name="l">The l.</param>
|
||||
/// <param name="r">The r.</param>
|
||||
private void UpdateAdjacency(ConvexFaceInternal l, ConvexFaceInternal r)
|
||||
{
|
||||
var lv = l.Vertices;
|
||||
var rv = r.Vertices;
|
||||
int i;
|
||||
|
||||
// reset marks on the 1st face
|
||||
for (i = 0; i < lv.Length; i++) VertexVisited[lv[i]] = false;
|
||||
|
||||
// mark all vertices on the 2nd face
|
||||
for (i = 0; i < rv.Length; i++) VertexVisited[rv[i]] = true;
|
||||
|
||||
// find the 1st false index
|
||||
for (i = 0; i < lv.Length; i++) if (!VertexVisited[lv[i]]) break;
|
||||
|
||||
// no vertex was marked
|
||||
if (i == NumOfDimensions) return;
|
||||
|
||||
// check if only 1 vertex wasn't marked
|
||||
for (var j = i + 1; j < lv.Length; j++) if (!VertexVisited[lv[j]]) return;
|
||||
|
||||
// if we are here, the two faces share an edge
|
||||
l.AdjacentFaces[i] = r.Index;
|
||||
|
||||
// update the adj. face on the other face - find the vertex that remains marked
|
||||
for (i = 0; i < lv.Length; i++) VertexVisited[lv[i]] = false;
|
||||
for (i = 0; i < rv.Length; i++)
|
||||
{
|
||||
if (VertexVisited[rv[i]]) break;
|
||||
}
|
||||
r.AdjacentFaces[i] = l.Index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used in the "initialization" code.
|
||||
/// </summary>
|
||||
/// <param name="face">The face.</param>
|
||||
private void FindBeyondVertices(ConvexFaceInternal face)
|
||||
{
|
||||
var beyondVertices = face.VerticesBeyond;
|
||||
MaxDistance = double.NegativeInfinity;
|
||||
FurthestVertex = 0;
|
||||
for (var i = 0; i < NumberOfVertices; i++)
|
||||
{
|
||||
if (VertexVisited[i]) continue;
|
||||
IsBeyond(face, beyondVertices, i);
|
||||
}
|
||||
|
||||
face.FurthestVertex = FurthestVertex;
|
||||
}
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to the dimension of the data.
|
||||
/// When the "lifted" hull is computed, Dimension is automatically incremented by one.
|
||||
/// </summary>
|
||||
internal readonly int NumOfDimensions;
|
||||
|
||||
/// <summary>
|
||||
/// Are we on a paraboloid?
|
||||
/// </summary>
|
||||
private readonly bool IsLifted;
|
||||
|
||||
/// <summary>
|
||||
/// Explained in ConvexHullComputationConfig.
|
||||
/// </summary>
|
||||
private readonly double PlaneDistanceTolerance;
|
||||
|
||||
/*
|
||||
* Representation of the input vertices.
|
||||
*
|
||||
* - In the algorithm, a vertex is represented by its index in the Vertices array.
|
||||
* This makes the algorithm a lot faster (up to 30%) than using object reference everywhere.
|
||||
* - Positions are stored as a single array of values. Coordinates for vertex with index i
|
||||
* are stored at indices <i * Dimension, (i + 1) * Dimension)
|
||||
* - VertexMarks are used by the algorithm to help identify a set of vertices that is "above" (or "beyond")
|
||||
* a specific face.
|
||||
*/
|
||||
/// <summary>
|
||||
/// The vertices
|
||||
/// </summary>
|
||||
private readonly IVertex[] Vertices;
|
||||
/// <summary>
|
||||
/// The positions
|
||||
/// </summary>
|
||||
private double[] Positions;
|
||||
/// <summary>
|
||||
/// The vertex marks
|
||||
/// </summary>
|
||||
private readonly bool[] VertexVisited;
|
||||
|
||||
private readonly int NumberOfVertices;
|
||||
|
||||
/*
|
||||
* The triangulation faces are represented in a single pool for objects that are being reused.
|
||||
* This allows for represent the faces as integers and significantly speeds up many computations.
|
||||
* - AffectedFaceFlags are used to mark affected faces/
|
||||
*/
|
||||
/// <summary>
|
||||
/// The face pool
|
||||
/// </summary>
|
||||
internal ConvexFaceInternal[] FacePool;
|
||||
/// <summary>
|
||||
/// The affected face flags
|
||||
/// </summary>
|
||||
internal bool[] AffectedFaceFlags;
|
||||
|
||||
/// <summary>
|
||||
/// Used to track the size of the current hull in the Update/RollbackCenter functions.
|
||||
/// </summary>
|
||||
private int ConvexHullSize;
|
||||
|
||||
/// <summary>
|
||||
/// A list of faces that that are not a part of the final convex hull and still need to be processed.
|
||||
/// </summary>
|
||||
private readonly FaceList UnprocessedFaces;
|
||||
|
||||
/// <summary>
|
||||
/// A list of faces that form the convex hull.
|
||||
/// </summary>
|
||||
private readonly IndexBuffer ConvexFaces;
|
||||
|
||||
/// <summary>
|
||||
/// The vertex that is currently being processed.
|
||||
/// </summary>
|
||||
private int CurrentVertex;
|
||||
|
||||
/// <summary>
|
||||
/// A helper variable to determine the furthest vertex for a particular convex face.
|
||||
/// </summary>
|
||||
private double MaxDistance;
|
||||
|
||||
/// <summary>
|
||||
/// A helper variable to help determine the index of the vertex that is furthest from the face that is currently being
|
||||
/// processed.
|
||||
/// </summary>
|
||||
private int FurthestVertex;
|
||||
|
||||
/// <summary>
|
||||
/// The centroid of the currently computed hull.
|
||||
/// </summary>
|
||||
private readonly double[] Center;
|
||||
|
||||
/*
|
||||
* Helper arrays to store faces for adjacency update.
|
||||
* This is just to prevent unnecessary allocations.
|
||||
*/
|
||||
/// <summary>
|
||||
/// The update buffer
|
||||
/// </summary>
|
||||
private readonly int[] UpdateBuffer;
|
||||
/// <summary>
|
||||
/// The update indices
|
||||
/// </summary>
|
||||
private readonly int[] UpdateIndices;
|
||||
|
||||
/// <summary>
|
||||
/// Used to determine which faces need to be updated at each step of the algorithm.
|
||||
/// </summary>
|
||||
private readonly IndexBuffer TraverseStack;
|
||||
|
||||
/// <summary>
|
||||
/// Used for VerticesBeyond for faces that are on the convex hull.
|
||||
/// </summary>
|
||||
private readonly IndexBuffer EmptyBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// Used to determine which vertices are "above" (or "beyond") a face
|
||||
/// </summary>
|
||||
private IndexBuffer BeyondBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// Stores faces that are visible from the current vertex.
|
||||
/// </summary>
|
||||
private readonly IndexBuffer AffectedFaceBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// Stores faces that form a "cone" created by adding new vertex.
|
||||
/// </summary>
|
||||
private readonly SimpleList<DeferredFace> ConeFaceBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// Stores a list of "singular" (or "generate", "planar", etc.) vertices that cannot be part of the hull.
|
||||
/// </summary>
|
||||
private readonly HashSet<int> SingularVertices;
|
||||
|
||||
/// <summary>
|
||||
/// The connector table helps to determine the adjacency of convex faces.
|
||||
/// Hashing is used instead of pairwise comparison. This significantly speeds up the computations,
|
||||
/// especially for higher dimensions.
|
||||
/// </summary>
|
||||
private readonly ConnectorList[] ConnectorTable;
|
||||
|
||||
/// <summary>
|
||||
/// Manages the memory allocations and storage of unused objects.
|
||||
/// Saves the garbage collector a lot of work.
|
||||
/// </summary>
|
||||
private readonly ObjectManager ObjectManager;
|
||||
|
||||
/// <summary>
|
||||
/// Helper class for handling math related stuff.
|
||||
/// </summary>
|
||||
private readonly MathHelper mathHelper;
|
||||
private readonly List<int>[] boundingBoxPoints;
|
||||
private int indexOfDimensionWithLeastExtremes;
|
||||
private readonly double[] minima;
|
||||
private readonly double[] maxima;
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 48bd00cb6ec8d244f90e1c1608f39074
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,635 +0,0 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MIConvexHull
|
||||
{
|
||||
/*
|
||||
* Main part of the algorithm
|
||||
* Basic idea:
|
||||
* - Create the initial hull (done in Initialize.cs)
|
||||
*
|
||||
* For each face there are "vertices beyond" which are "visible" from it.
|
||||
* If there are no such vertices, the face is on the hull.
|
||||
*
|
||||
* - While there is at least one face with at least one "vertex beyond":
|
||||
* * Pick the furthest beyond vertex
|
||||
* * For this vertex:
|
||||
* > find all faces that are visible from it (TagAffectedFaces)
|
||||
* > remove them and replace them with a "cone" created by the vertex and the boundary
|
||||
* of the affected faces, and for each new face, compute "beyond vertices"
|
||||
* (CreateCone + CommitCone)
|
||||
*
|
||||
* + Implement it in way that is fast, but hard to understand and maintain.
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Class ConvexHullAlgorithm.
|
||||
/// </summary>
|
||||
internal partial class ConvexHullAlgorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// Tags all faces seen from the current vertex with 1.
|
||||
/// </summary>
|
||||
/// <param name="currentFace">The current face.</param>
|
||||
private void TagAffectedFaces(ConvexFaceInternal currentFace)
|
||||
{
|
||||
AffectedFaceBuffer.Clear();
|
||||
AffectedFaceBuffer.Add(currentFace.Index);
|
||||
TraverseAffectedFaces(currentFace.Index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursively traverse all the relevant faces.
|
||||
/// </summary>
|
||||
/// <param name="currentFace">The current face.</param>
|
||||
private void TraverseAffectedFaces(int currentFace)
|
||||
{
|
||||
TraverseStack.Clear();
|
||||
TraverseStack.Push(currentFace);
|
||||
AffectedFaceFlags[currentFace] = true;
|
||||
|
||||
while (TraverseStack.Count > 0)
|
||||
{
|
||||
var top = FacePool[TraverseStack.Pop()];
|
||||
for (var i = 0; i < NumOfDimensions; i++)
|
||||
{
|
||||
var adjFace = top.AdjacentFaces[i];
|
||||
|
||||
if (!AffectedFaceFlags[adjFace] &&
|
||||
mathHelper.GetVertexDistance(CurrentVertex, FacePool[adjFace]) >= PlaneDistanceTolerance)
|
||||
{
|
||||
AffectedFaceBuffer.Add(adjFace);
|
||||
AffectedFaceFlags[adjFace] = true;
|
||||
TraverseStack.Push(adjFace);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new deferred face.
|
||||
/// </summary>
|
||||
/// <param name="face">The face.</param>
|
||||
/// <param name="faceIndex">Index of the face.</param>
|
||||
/// <param name="pivot">The pivot.</param>
|
||||
/// <param name="pivotIndex">Index of the pivot.</param>
|
||||
/// <param name="oldFace">The old face.</param>
|
||||
/// <returns>DeferredFace.</returns>
|
||||
private DeferredFace MakeDeferredFace(ConvexFaceInternal face, int faceIndex, ConvexFaceInternal pivot,
|
||||
int pivotIndex, ConvexFaceInternal oldFace)
|
||||
{
|
||||
var ret = ObjectManager.GetDeferredFace();
|
||||
|
||||
ret.Face = face;
|
||||
ret.FaceIndex = faceIndex;
|
||||
ret.Pivot = pivot;
|
||||
ret.PivotIndex = pivotIndex;
|
||||
ret.OldFace = oldFace;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connect faces using a connector.
|
||||
/// </summary>
|
||||
/// <param name="connector">The connector.</param>
|
||||
private void ConnectFace(FaceConnector connector)
|
||||
{
|
||||
var index = connector.HashCode % Constants.ConnectorTableSize;
|
||||
var list = ConnectorTable[index];
|
||||
|
||||
for (var current = list.First; current != null; current = current.Next)
|
||||
{
|
||||
if (FaceConnector.AreConnectable(connector, current, NumOfDimensions))
|
||||
{
|
||||
list.Remove(current);
|
||||
FaceConnector.Connect(current, connector);
|
||||
current.Face = null;
|
||||
connector.Face = null;
|
||||
ObjectManager.DepositConnector(current);
|
||||
ObjectManager.DepositConnector(connector);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
list.Add(connector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the faces "covered" by the current vertex and adds the newly created ones.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
|
||||
private bool CreateCone()
|
||||
{
|
||||
var currentVertexIndex = CurrentVertex;
|
||||
ConeFaceBuffer.Clear();
|
||||
|
||||
for (var fIndex = 0; fIndex < AffectedFaceBuffer.Count; fIndex++)
|
||||
{
|
||||
var oldFaceIndex = AffectedFaceBuffer[fIndex];
|
||||
var oldFace = FacePool[oldFaceIndex];
|
||||
|
||||
// Find the faces that need to be updated
|
||||
var updateCount = 0;
|
||||
for (var i = 0; i < NumOfDimensions; i++)
|
||||
{
|
||||
var af = oldFace.AdjacentFaces[i];
|
||||
if (!AffectedFaceFlags[af]) // Tag == false when oldFaces does not contain af
|
||||
{
|
||||
UpdateBuffer[updateCount] = af;
|
||||
UpdateIndices[updateCount] = i;
|
||||
++updateCount;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < updateCount; i++)
|
||||
{
|
||||
var adjacentFace = FacePool[UpdateBuffer[i]];
|
||||
|
||||
var oldFaceAdjacentIndex = 0;
|
||||
var adjFaceAdjacency = adjacentFace.AdjacentFaces;
|
||||
for (var j = 0; j < adjFaceAdjacency.Length; j++)
|
||||
{
|
||||
if (oldFaceIndex == adjFaceAdjacency[j])
|
||||
{
|
||||
oldFaceAdjacentIndex = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var forbidden = UpdateIndices[i]; // Index of the face that corresponds to this adjacent face
|
||||
|
||||
int oldVertexIndex;
|
||||
int[] vertices;
|
||||
|
||||
var newFaceIndex = ObjectManager.GetFace();
|
||||
var newFace = FacePool[newFaceIndex];
|
||||
vertices = newFace.Vertices;
|
||||
for (var j = 0; j < NumOfDimensions; j++) vertices[j] = oldFace.Vertices[j];
|
||||
oldVertexIndex = vertices[forbidden];
|
||||
|
||||
int orderedPivotIndex;
|
||||
|
||||
// correct the ordering
|
||||
if (currentVertexIndex < oldVertexIndex)
|
||||
{
|
||||
orderedPivotIndex = 0;
|
||||
for (var j = forbidden - 1; j >= 0; j--)
|
||||
{
|
||||
if (vertices[j] > currentVertexIndex) vertices[j + 1] = vertices[j];
|
||||
else
|
||||
{
|
||||
orderedPivotIndex = j + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
orderedPivotIndex = NumOfDimensions - 1;
|
||||
for (var j = forbidden + 1; j < NumOfDimensions; j++)
|
||||
{
|
||||
if (vertices[j] < currentVertexIndex) vertices[j - 1] = vertices[j];
|
||||
else
|
||||
{
|
||||
orderedPivotIndex = j - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vertices[orderedPivotIndex] = CurrentVertex;
|
||||
|
||||
if (!mathHelper.CalculateFacePlane(newFace, Center))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ConeFaceBuffer.Add(MakeDeferredFace(newFace, orderedPivotIndex, adjacentFace, oldFaceAdjacentIndex,
|
||||
oldFace));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Commits a cone and adds a vertex to the convex hull.
|
||||
/// </summary>
|
||||
private void CommitCone()
|
||||
{
|
||||
// Fill the adjacency.
|
||||
for (var i = 0; i < ConeFaceBuffer.Count; i++)
|
||||
{
|
||||
var face = ConeFaceBuffer[i];
|
||||
|
||||
var newFace = face.Face;
|
||||
var adjacentFace = face.Pivot;
|
||||
var oldFace = face.OldFace;
|
||||
var orderedPivotIndex = face.FaceIndex;
|
||||
|
||||
newFace.AdjacentFaces[orderedPivotIndex] = adjacentFace.Index;
|
||||
adjacentFace.AdjacentFaces[face.PivotIndex] = newFace.Index;
|
||||
|
||||
// let there be a connection.
|
||||
for (var j = 0; j < NumOfDimensions; j++)
|
||||
{
|
||||
if (j == orderedPivotIndex) continue;
|
||||
var connector = ObjectManager.GetConnector();
|
||||
connector.Update(newFace, j, NumOfDimensions);
|
||||
ConnectFace(connector);
|
||||
}
|
||||
|
||||
// the id adjacent face on the hull? If so, we can use simple method to find beyond vertices.
|
||||
if (adjacentFace.VerticesBeyond.Count == 0)
|
||||
FindBeyondVertices(newFace, oldFace.VerticesBeyond);
|
||||
// it is slightly more effective if the face with the lower number of beyond vertices comes first.
|
||||
else if (adjacentFace.VerticesBeyond.Count < oldFace.VerticesBeyond.Count)
|
||||
FindBeyondVertices(newFace, adjacentFace.VerticesBeyond, oldFace.VerticesBeyond);
|
||||
else
|
||||
FindBeyondVertices(newFace, oldFace.VerticesBeyond, adjacentFace.VerticesBeyond);
|
||||
|
||||
// This face will definitely lie on the hull
|
||||
if (newFace.VerticesBeyond.Count == 0)
|
||||
{
|
||||
ConvexFaces.Add(newFace.Index);
|
||||
UnprocessedFaces.Remove(newFace);
|
||||
ObjectManager.DepositVertexBuffer(newFace.VerticesBeyond);
|
||||
newFace.VerticesBeyond = EmptyBuffer;
|
||||
}
|
||||
else // Add the face to the list
|
||||
{
|
||||
UnprocessedFaces.Add(newFace);
|
||||
}
|
||||
|
||||
// recycle the object.
|
||||
ObjectManager.DepositDeferredFace(face);
|
||||
}
|
||||
|
||||
// Recycle the affected faces.
|
||||
for (var fIndex = 0; fIndex < AffectedFaceBuffer.Count; fIndex++)
|
||||
{
|
||||
var face = AffectedFaceBuffer[fIndex];
|
||||
UnprocessedFaces.Remove(FacePool[face]);
|
||||
ObjectManager.DepositFace(face);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check whether the vertex v is beyond the given face. If so, add it to beyondVertices.
|
||||
/// </summary>
|
||||
/// <param name="face">The face.</param>
|
||||
/// <param name="beyondVertices">The beyond vertices.</param>
|
||||
/// <param name="v">The v.</param>
|
||||
private void IsBeyond(ConvexFaceInternal face, IndexBuffer beyondVertices, int v)
|
||||
{
|
||||
var distance = mathHelper.GetVertexDistance(v, face);
|
||||
if (distance >= PlaneDistanceTolerance)
|
||||
{
|
||||
if (distance > MaxDistance)
|
||||
{
|
||||
// If it's within the tolerance distance, use the lex. larger point
|
||||
if (distance - MaxDistance < PlaneDistanceTolerance)
|
||||
{ // todo: why is this LexCompare necessary. Would seem to favor x over y over z (etc.)?
|
||||
if (LexCompare(v, FurthestVertex) > 0)
|
||||
{
|
||||
MaxDistance = distance;
|
||||
FurthestVertex = v;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MaxDistance = distance;
|
||||
FurthestVertex = v;
|
||||
}
|
||||
}
|
||||
beyondVertices.Add(v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Compares the values of two vertices. The return value (-1, 0 or +1) are found
|
||||
/// by first checking the first coordinate and then progressing through the rest.
|
||||
/// In this way {2, 8} will be a "-1" (less than) {3, 1}.
|
||||
/// </summary>
|
||||
/// <param name="u">The base vertex index, u.</param>
|
||||
/// <param name="v">The compared vertex index, v.</param>
|
||||
/// <returns>System.Int32.</returns>
|
||||
private int LexCompare(int u, int v)
|
||||
{
|
||||
int uOffset = u * NumOfDimensions, vOffset = v * NumOfDimensions;
|
||||
for (var i = 0; i < NumOfDimensions; i++)
|
||||
{
|
||||
double x = Positions[uOffset + i], y = Positions[vOffset + i];
|
||||
var comp = x.CompareTo(y);
|
||||
if (comp != 0) return comp;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Used by update faces.
|
||||
/// </summary>
|
||||
/// <param name="face">The face.</param>
|
||||
/// <param name="beyond">The beyond.</param>
|
||||
/// <param name="beyond1">The beyond1.</param>
|
||||
private void FindBeyondVertices(ConvexFaceInternal face, IndexBuffer beyond, IndexBuffer beyond1)
|
||||
{
|
||||
var beyondVertices = BeyondBuffer;
|
||||
|
||||
MaxDistance = double.NegativeInfinity;
|
||||
FurthestVertex = 0;
|
||||
int v;
|
||||
|
||||
for (var i = 0; i < beyond1.Count; i++) VertexVisited[beyond1[i]] = true;
|
||||
VertexVisited[CurrentVertex] = false;
|
||||
for (var i = 0; i < beyond.Count; i++)
|
||||
{
|
||||
v = beyond[i];
|
||||
if (v == CurrentVertex) continue;
|
||||
VertexVisited[v] = false;
|
||||
IsBeyond(face, beyondVertices, v);
|
||||
}
|
||||
|
||||
for (var i = 0; i < beyond1.Count; i++)
|
||||
{
|
||||
v = beyond1[i];
|
||||
if (VertexVisited[v]) IsBeyond(face, beyondVertices, v);
|
||||
}
|
||||
|
||||
face.FurthestVertex = FurthestVertex;
|
||||
|
||||
// Pull the old switch a roo (switch the face beyond buffers)
|
||||
var temp = face.VerticesBeyond;
|
||||
face.VerticesBeyond = beyondVertices;
|
||||
if (temp.Count > 0) temp.Clear();
|
||||
BeyondBuffer = temp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the beyond vertices.
|
||||
/// </summary>
|
||||
/// <param name="face">The face.</param>
|
||||
/// <param name="beyond">The beyond.</param>
|
||||
private void FindBeyondVertices(ConvexFaceInternal face, IndexBuffer beyond)
|
||||
{
|
||||
var beyondVertices = BeyondBuffer;
|
||||
|
||||
MaxDistance = double.NegativeInfinity;
|
||||
FurthestVertex = 0;
|
||||
int v;
|
||||
|
||||
for (var i = 0; i < beyond.Count; i++)
|
||||
{
|
||||
v = beyond[i];
|
||||
if (v == CurrentVertex) continue;
|
||||
IsBeyond(face, beyondVertices, v);
|
||||
}
|
||||
|
||||
face.FurthestVertex = FurthestVertex;
|
||||
|
||||
// Pull the old switch a roo (switch the face beyond buffers)
|
||||
var temp = face.VerticesBeyond;
|
||||
face.VerticesBeyond = beyondVertices;
|
||||
if (temp.Count > 0) temp.Clear();
|
||||
BeyondBuffer = temp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recalculates the centroid of the current hull.
|
||||
/// </summary>
|
||||
private void UpdateCenter()
|
||||
{
|
||||
for (var i = 0; i < NumOfDimensions; i++) Center[i] *= ConvexHullSize;
|
||||
ConvexHullSize += 1;
|
||||
var f = 1.0 / ConvexHullSize;
|
||||
var co = CurrentVertex * NumOfDimensions;
|
||||
for (var i = 0; i < NumOfDimensions; i++) Center[i] = f * (Center[i] + Positions[co + i]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the last vertex from the center.
|
||||
/// </summary>
|
||||
private void RollbackCenter()
|
||||
{
|
||||
for (var i = 0; i < NumOfDimensions; i++) Center[i] *= ConvexHullSize;
|
||||
ConvexHullSize -= 1;
|
||||
var f = ConvexHullSize > 0 ? 1.0 / ConvexHullSize : 0.0;
|
||||
var co = CurrentVertex * NumOfDimensions;
|
||||
for (var i = 0; i < NumOfDimensions; i++) Center[i] = f * (Center[i] - Positions[co + i]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles singular vertex.
|
||||
/// </summary>
|
||||
private void HandleSingular()
|
||||
{
|
||||
RollbackCenter();
|
||||
SingularVertices.Add(CurrentVertex);
|
||||
|
||||
// This means that all the affected faces must be on the hull and that all their "vertices beyond" are singular.
|
||||
for (var fIndex = 0; fIndex < AffectedFaceBuffer.Count; fIndex++)
|
||||
{
|
||||
var face = FacePool[AffectedFaceBuffer[fIndex]];
|
||||
var vb = face.VerticesBeyond;
|
||||
for (var i = 0; i < vb.Count; i++)
|
||||
{
|
||||
SingularVertices.Add(vb[i]);
|
||||
}
|
||||
|
||||
ConvexFaces.Add(face.Index);
|
||||
UnprocessedFaces.Remove(face);
|
||||
ObjectManager.DepositVertexBuffer(face.VerticesBeyond);
|
||||
face.VerticesBeyond = EmptyBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a vertex coordinate. In order to reduce speed, all vertex coordinates
|
||||
/// have been placed in a single array.
|
||||
/// </summary>
|
||||
/// <param name="vIndex">The vertex index.</param>
|
||||
/// <param name="dimension">The index of the dimension.</param>
|
||||
/// <returns>System.Double.</returns>
|
||||
private double GetCoordinate(int vIndex, int dimension)
|
||||
{
|
||||
return Positions[vIndex * NumOfDimensions + dimension];
|
||||
}
|
||||
|
||||
#region Returning the Results in the proper format
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hull vertices.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex">The type of the t vertex.</typeparam>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <returns>TVertex[].</returns>
|
||||
private TVertex[] GetHullVertices<TVertex>(IList<TVertex> data)
|
||||
{
|
||||
var cellCount = ConvexFaces.Count;
|
||||
var hullVertexCount = 0;
|
||||
|
||||
for (var i = 0; i < NumberOfVertices; i++) VertexVisited[i] = false;
|
||||
|
||||
for (var i = 0; i < cellCount; i++)
|
||||
{
|
||||
var vs = FacePool[ConvexFaces[i]].Vertices;
|
||||
for (var j = 0; j < vs.Length; j++)
|
||||
{
|
||||
var v = vs[j];
|
||||
if (!VertexVisited[v])
|
||||
{
|
||||
VertexVisited[v] = true;
|
||||
hullVertexCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var result = new TVertex[hullVertexCount];
|
||||
for (var i = 0; i < NumberOfVertices; i++)
|
||||
{
|
||||
if (VertexVisited[i]) result[--hullVertexCount] = data[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the convex hull and creates the TFace objects.
|
||||
/// </summary>
|
||||
/// <typeparam name="TFace">The type of the t face.</typeparam>
|
||||
/// <typeparam name="TVertex">The type of the t vertex.</typeparam>
|
||||
/// <returns>TFace[].</returns>
|
||||
private TFace[] GetConvexFaces<TVertex, TFace>()
|
||||
where TFace : ConvexFace<TVertex, TFace>, new()
|
||||
where TVertex : IVertex
|
||||
{
|
||||
var faces = ConvexFaces;
|
||||
var cellCount = faces.Count;
|
||||
var cells = new TFace[cellCount];
|
||||
|
||||
for (var i = 0; i < cellCount; i++)
|
||||
{
|
||||
var face = FacePool[faces[i]];
|
||||
var vertices = new TVertex[NumOfDimensions];
|
||||
for (var j = 0; j < NumOfDimensions; j++)
|
||||
{
|
||||
vertices[j] = (TVertex)Vertices[face.Vertices[j]];
|
||||
}
|
||||
|
||||
cells[i] = new TFace
|
||||
{
|
||||
Vertices = vertices,
|
||||
Adjacency = new TFace[NumOfDimensions],
|
||||
Normal = IsLifted ? null : face.Normal
|
||||
};
|
||||
face.Tag = i;
|
||||
}
|
||||
|
||||
for (var i = 0; i < cellCount; i++)
|
||||
{
|
||||
var face = FacePool[faces[i]];
|
||||
var cell = cells[i];
|
||||
for (var j = 0; j < NumOfDimensions; j++)
|
||||
{
|
||||
if (face.AdjacentFaces[j] < 0) continue;
|
||||
cell.Adjacency[j] = cells[FacePool[face.AdjacentFaces[j]].Tag];
|
||||
}
|
||||
|
||||
// Fix the vertex orientation.
|
||||
if (face.IsNormalFlipped)
|
||||
{
|
||||
var tempVert = cell.Vertices[0];
|
||||
cell.Vertices[0] = cell.Vertices[NumOfDimensions - 1];
|
||||
cell.Vertices[NumOfDimensions - 1] = tempVert;
|
||||
|
||||
var tempAdj = cell.Adjacency[0];
|
||||
cell.Adjacency[0] = cell.Adjacency[NumOfDimensions - 1];
|
||||
cell.Adjacency[NumOfDimensions - 1] = tempAdj;
|
||||
}
|
||||
}
|
||||
|
||||
return cells;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For 2D only: Returns the result in counter-clockwise order starting with the element with the lowest X value.
|
||||
/// If there are multiple vertices with the same minimum X, then the one with the lowest Y is chosen.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex">The type of the vertex.</typeparam>
|
||||
/// <typeparam name="TFace">The type of the face.</typeparam>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <returns></returns>
|
||||
private ConvexHull<TVertex, TFace> Return2DResultInOrder<TVertex, TFace>(IList<TVertex> data)
|
||||
where TVertex : IVertex
|
||||
where TFace : ConvexFace<TVertex, TFace>, new()
|
||||
{
|
||||
TFace[] faces = GetConvexFaces<TVertex, TFace>();
|
||||
var numPoints = faces.Length;
|
||||
var orderDictionary = new Dictionary<TVertex, TFace>();
|
||||
foreach (var face in faces)
|
||||
orderDictionary.Add(face.Vertices[1], face);
|
||||
var firstPoint = faces[0].Vertices[1];
|
||||
var nextPoint = faces[0].Vertices[0];
|
||||
var orderedPointList = new List<TVertex>();
|
||||
orderedPointList.Add(firstPoint);
|
||||
var orderedFaceList = new List<TFace>();
|
||||
orderedFaceList.Add(faces[1]);
|
||||
var lowestXMinIndex = 0;
|
||||
var k = 0;
|
||||
while (!nextPoint.Equals(firstPoint))
|
||||
{
|
||||
orderedPointList.Add(nextPoint);
|
||||
var nextFace = orderDictionary[nextPoint];
|
||||
orderedFaceList.Add(nextFace);
|
||||
if (nextPoint.Position[0] < orderedPointList[lowestXMinIndex].Position[0]
|
||||
|| (nextPoint.Position[0] == orderedPointList[lowestXMinIndex].Position[0]
|
||||
&& nextPoint.Position[1] <= orderedPointList[lowestXMinIndex].Position[1]))
|
||||
lowestXMinIndex = k;
|
||||
k++;
|
||||
nextPoint = nextFace.Vertices[0];
|
||||
}
|
||||
TVertex[] points = new TVertex[numPoints];
|
||||
for (int i = 0; i < numPoints; i++)
|
||||
{
|
||||
var j = (i + lowestXMinIndex) % numPoints;
|
||||
points[i] = orderedPointList[j];
|
||||
faces[i] = orderedFaceList[j];
|
||||
}
|
||||
return new ConvexHull<TVertex, TFace>
|
||||
{
|
||||
Points = points,
|
||||
Faces = faces
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f7f265217d00ea340b87645242a45181
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,237 +0,0 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
namespace MIConvexHull
|
||||
{
|
||||
/// <summary>
|
||||
/// For deferred face addition.
|
||||
/// </summary>
|
||||
internal sealed class DeferredFace
|
||||
{
|
||||
/// <summary>
|
||||
/// The faces.
|
||||
/// </summary>
|
||||
public ConvexFaceInternal Face, Pivot, OldFace;
|
||||
|
||||
/// <summary>
|
||||
/// The indices.
|
||||
/// </summary>
|
||||
public int FaceIndex, PivotIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A helper class used to connect faces.
|
||||
/// </summary>
|
||||
internal sealed class FaceConnector
|
||||
{
|
||||
/// <summary>
|
||||
/// The edge to be connected.
|
||||
/// </summary>
|
||||
public int EdgeIndex;
|
||||
|
||||
/// <summary>
|
||||
/// The face.
|
||||
/// </summary>
|
||||
public ConvexFaceInternal Face;
|
||||
|
||||
/// <summary>
|
||||
/// The hash code computed from indices.
|
||||
/// </summary>
|
||||
public uint HashCode;
|
||||
|
||||
/// <summary>
|
||||
/// Next node in the list.
|
||||
/// </summary>
|
||||
public FaceConnector Next;
|
||||
|
||||
/// <summary>
|
||||
/// Prev node in the list.
|
||||
/// </summary>
|
||||
public FaceConnector Previous;
|
||||
|
||||
/// <summary>
|
||||
/// The vertex indices.
|
||||
/// </summary>
|
||||
public int[] Vertices;
|
||||
|
||||
/// <summary>
|
||||
/// Ctor.
|
||||
/// </summary>
|
||||
/// <param name="dimension">The dimension.</param>
|
||||
public FaceConnector(int dimension)
|
||||
{
|
||||
Vertices = new int[dimension - 1];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the connector.
|
||||
/// </summary>
|
||||
/// <param name="face">The face.</param>
|
||||
/// <param name="edgeIndex">Index of the edge.</param>
|
||||
/// <param name="dim">The dim.</param>
|
||||
public void Update(ConvexFaceInternal face, int edgeIndex, int dim)
|
||||
{
|
||||
Face = face;
|
||||
EdgeIndex = edgeIndex;
|
||||
|
||||
uint hashCode = 23;
|
||||
|
||||
unchecked
|
||||
{
|
||||
var vs = face.Vertices;
|
||||
int i, c = 0;
|
||||
for (i = 0; i < edgeIndex; i++)
|
||||
{
|
||||
Vertices[c++] = vs[i];
|
||||
hashCode += 31*hashCode + (uint) vs[i];
|
||||
}
|
||||
for (i = edgeIndex + 1; i < vs.Length; i++)
|
||||
{
|
||||
Vertices[c++] = vs[i];
|
||||
hashCode += 31*hashCode + (uint) vs[i];
|
||||
}
|
||||
}
|
||||
|
||||
HashCode = hashCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Can two faces be connected.
|
||||
/// </summary>
|
||||
/// <param name="a">a.</param>
|
||||
/// <param name="b">The b.</param>
|
||||
/// <param name="dim">The dim.</param>
|
||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
|
||||
public static bool AreConnectable(FaceConnector a, FaceConnector b, int dim)
|
||||
{
|
||||
if (a.HashCode != b.HashCode) return false;
|
||||
|
||||
var av = a.Vertices;
|
||||
var bv = b.Vertices;
|
||||
for (var i = 0; i < av.Length; i++)
|
||||
{
|
||||
if (av[i] != bv[i]) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connect two faces.
|
||||
/// </summary>
|
||||
/// <param name="a">a.</param>
|
||||
/// <param name="b">The b.</param>
|
||||
public static void Connect(FaceConnector a, FaceConnector b)
|
||||
{
|
||||
a.Face.AdjacentFaces[a.EdgeIndex] = b.Face.Index;
|
||||
b.Face.AdjacentFaces[b.EdgeIndex] = a.Face.Index;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This internal class manages the faces of the convex hull. It is a
|
||||
/// separate class from the desired user class.
|
||||
/// </summary>
|
||||
internal sealed class ConvexFaceInternal
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the adjacent face data.
|
||||
/// </summary>
|
||||
public int[] AdjacentFaces;
|
||||
|
||||
/// <summary>
|
||||
/// The furthest vertex.
|
||||
/// </summary>
|
||||
public int FurthestVertex;
|
||||
|
||||
/// <summary>
|
||||
/// Index of the face inside the pool.
|
||||
/// </summary>
|
||||
public int Index;
|
||||
|
||||
/// <summary>
|
||||
/// Is it present in the list.
|
||||
/// </summary>
|
||||
public bool InList;
|
||||
|
||||
/// <summary>
|
||||
/// Is the normal flipped?
|
||||
/// </summary>
|
||||
public bool IsNormalFlipped;
|
||||
|
||||
/// <summary>
|
||||
/// Next node in the list.
|
||||
/// </summary>
|
||||
public ConvexFaceInternal Next;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the normal vector.
|
||||
/// </summary>
|
||||
public double[] Normal;
|
||||
|
||||
/// <summary>
|
||||
/// Face plane constant element.
|
||||
/// </summary>
|
||||
public double Offset;
|
||||
|
||||
//public int UnprocessedIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Prev node in the list.
|
||||
/// </summary>
|
||||
public ConvexFaceInternal Previous;
|
||||
|
||||
/// <summary>
|
||||
/// Used to traverse affected faces and create the Delaunay representation.
|
||||
/// </summary>
|
||||
public int Tag;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the vertices.
|
||||
/// </summary>
|
||||
public int[] Vertices;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the vertices beyond.
|
||||
/// </summary>
|
||||
public IndexBuffer VerticesBeyond;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConvexFaceInternal" /> class.
|
||||
/// </summary>
|
||||
/// <param name="dimension">The dimension.</param>
|
||||
/// <param name="index">The index.</param>
|
||||
/// <param name="beyondList">The beyond list.</param>
|
||||
public ConvexFaceInternal(int dimension, int index, IndexBuffer beyondList)
|
||||
{
|
||||
Index = index;
|
||||
AdjacentFaces = new int[dimension];
|
||||
VerticesBeyond = beyondList;
|
||||
Normal = new double[dimension];
|
||||
Vertices = new int[dimension];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 58c58e163f4c86e48a6df3ee6c5056c0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,476 +0,0 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MIConvexHull
|
||||
{
|
||||
/// <summary>
|
||||
/// A helper class mostly for normal computation. If convex hulls are computed
|
||||
/// in higher dimensions, it might be a good idea to add a specific
|
||||
/// FindNormalVectorND function.
|
||||
/// </summary>
|
||||
internal class MathHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// The dimension
|
||||
/// </summary>
|
||||
private readonly int Dimension;
|
||||
/// <summary>
|
||||
/// The matrix pivots
|
||||
/// </summary>
|
||||
private readonly int[] matrixPivots;
|
||||
/// <summary>
|
||||
/// The n d matrix
|
||||
/// </summary>
|
||||
private readonly double[] nDMatrix;
|
||||
/// <summary>
|
||||
/// The n d normal helper vector
|
||||
/// </summary>
|
||||
private readonly double[] nDNormalHelperVector;
|
||||
|
||||
/// <summary>
|
||||
/// The nt x
|
||||
/// </summary>
|
||||
private readonly double[] ntX;
|
||||
/// <summary>
|
||||
/// The nt y
|
||||
/// </summary>
|
||||
private readonly double[] ntY;
|
||||
/// <summary>
|
||||
/// The nt z
|
||||
/// </summary>
|
||||
private readonly double[] ntZ;
|
||||
|
||||
/// <summary>
|
||||
/// The position data
|
||||
/// </summary>
|
||||
private readonly double[] PositionData;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MathHelper"/> class.
|
||||
/// </summary>
|
||||
/// <param name="dimension">The dimension.</param>
|
||||
/// <param name="positions">The positions.</param>
|
||||
internal MathHelper(int dimension, double[] positions)
|
||||
{
|
||||
PositionData = positions;
|
||||
Dimension = dimension;
|
||||
|
||||
ntX = new double[Dimension];
|
||||
ntY = new double[Dimension];
|
||||
ntZ = new double[Dimension];
|
||||
|
||||
nDNormalHelperVector = new double[Dimension];
|
||||
nDMatrix = new double[Dimension * Dimension];
|
||||
matrixPivots = new int[Dimension];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the normal and offset of the hyper-plane given by the face's vertices.
|
||||
/// </summary>
|
||||
/// <param name="face">The face.</param>
|
||||
/// <param name="center">The center.</param>
|
||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
|
||||
internal bool CalculateFacePlane(ConvexFaceInternal face, double[] center)
|
||||
{
|
||||
var vertices = face.Vertices;
|
||||
var normal = face.Normal;
|
||||
FindNormalVector(vertices, normal);
|
||||
|
||||
if (double.IsNaN(normal[0]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var offset = 0.0;
|
||||
var centerDistance = 0.0;
|
||||
var fi = vertices[0] * Dimension;
|
||||
for (var i = 0; i < Dimension; i++)
|
||||
{
|
||||
var n = normal[i];
|
||||
offset += n * PositionData[fi + i];
|
||||
centerDistance += n * center[i];
|
||||
}
|
||||
face.Offset = -offset;
|
||||
centerDistance -= offset;
|
||||
|
||||
if (centerDistance > 0)
|
||||
{
|
||||
for (var i = 0; i < Dimension; i++) normal[i] = -normal[i];
|
||||
face.Offset = offset;
|
||||
face.IsNormalFlipped = true;
|
||||
}
|
||||
else face.IsNormalFlipped = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the vertex is "visible" from the face.
|
||||
/// The vertex is "over face" if the return value is > Constants.PlaneDistanceTolerance.
|
||||
/// </summary>
|
||||
/// <param name="v">The v.</param>
|
||||
/// <param name="f">The f.</param>
|
||||
/// <returns>The vertex is "over face" if the result is positive.</returns>
|
||||
internal double GetVertexDistance(int v, ConvexFaceInternal f)
|
||||
{
|
||||
var normal = f.Normal;
|
||||
var x = v * Dimension;
|
||||
var distance = f.Offset;
|
||||
for (var i = 0; i < normal.Length; i++) distance += normal[i] * PositionData[x + i];
|
||||
return distance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the vector the between vertices.
|
||||
/// </summary>
|
||||
/// <param name="toIndex">To index.</param>
|
||||
/// <param name="fromIndex">From index.</param>
|
||||
/// <returns>System.Double[].</returns>
|
||||
internal double[] VectorBetweenVertices(int toIndex, int fromIndex)
|
||||
{
|
||||
var target = new double[Dimension];
|
||||
VectorBetweenVertices(toIndex, fromIndex, target);
|
||||
return target;
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns the vector the between vertices.
|
||||
/// </summary>
|
||||
/// <param name="fromIndex">From index.</param>
|
||||
/// <param name="toIndex">To index.</param>
|
||||
/// <param name="target">The target.</param>
|
||||
/// <returns></returns>
|
||||
private void VectorBetweenVertices(int toIndex, int fromIndex, double[] target)
|
||||
{
|
||||
int u = toIndex * Dimension, v = fromIndex * Dimension;
|
||||
for (var i = 0; i < Dimension; i++)
|
||||
{
|
||||
target[i] = PositionData[u + i] - PositionData[v + i];
|
||||
}
|
||||
}
|
||||
|
||||
internal void RandomOffsetToLift(int index, double maxHeight)
|
||||
{
|
||||
var random = new Random();
|
||||
var liftIndex = (index * Dimension) + Dimension - 1;
|
||||
PositionData[liftIndex] += 0.0001 * maxHeight * (random.NextDouble() - 0.5);
|
||||
}
|
||||
#region Find the normal vector of the face
|
||||
/// <summary>
|
||||
/// Finds normal vector of a hyper-plane given by vertices.
|
||||
/// Stores the results to normalData.
|
||||
/// </summary>
|
||||
/// <param name="vertices">The vertices.</param>
|
||||
/// <param name="normalData">The normal data.</param>
|
||||
private void FindNormalVector(int[] vertices, double[] normalData)
|
||||
{
|
||||
switch (Dimension)
|
||||
{
|
||||
case 2:
|
||||
FindNormalVector2D(vertices, normalData);
|
||||
break;
|
||||
case 3:
|
||||
FindNormalVector3D(vertices, normalData);
|
||||
break;
|
||||
case 4:
|
||||
FindNormalVector4D(vertices, normalData);
|
||||
break;
|
||||
default:
|
||||
FindNormalVectorND(vertices, normalData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Finds 2D normal vector.
|
||||
/// </summary>
|
||||
/// <param name="vertices">The vertices.</param>
|
||||
/// <param name="normal">The normal.</param>
|
||||
private void FindNormalVector2D(int[] vertices, double[] normal)
|
||||
{
|
||||
VectorBetweenVertices(vertices[1], vertices[0], ntX);
|
||||
|
||||
var nx = -ntX[1];
|
||||
var ny = ntX[0];
|
||||
|
||||
var norm = Math.Sqrt(nx * nx + ny * ny);
|
||||
|
||||
var f = 1.0 / norm;
|
||||
normal[0] = f * nx;
|
||||
normal[1] = f * ny;
|
||||
}
|
||||
/// <summary>
|
||||
/// Finds 3D normal vector.
|
||||
/// </summary>
|
||||
/// <param name="vertices">The vertices.</param>
|
||||
/// <param name="normal">The normal.</param>
|
||||
private void FindNormalVector3D(int[] vertices, double[] normal)
|
||||
{
|
||||
VectorBetweenVertices(vertices[1], vertices[0], ntX);
|
||||
VectorBetweenVertices(vertices[2], vertices[1], ntY);
|
||||
|
||||
var nx = ntX[1] * ntY[2] - ntX[2] * ntY[1];
|
||||
var ny = ntX[2] * ntY[0] - ntX[0] * ntY[2];
|
||||
var nz = ntX[0] * ntY[1] - ntX[1] * ntY[0];
|
||||
|
||||
var norm = Math.Sqrt(nx * nx + ny * ny + nz * nz);
|
||||
|
||||
var f = 1.0 / norm;
|
||||
normal[0] = f * nx;
|
||||
normal[1] = f * ny;
|
||||
normal[2] = f * nz;
|
||||
}
|
||||
/// <summary>
|
||||
/// Finds 4D normal vector.
|
||||
/// </summary>
|
||||
/// <param name="vertices">The vertices.</param>
|
||||
/// <param name="normal">The normal.</param>
|
||||
private void FindNormalVector4D(int[] vertices, double[] normal)
|
||||
{
|
||||
VectorBetweenVertices(vertices[1], vertices[0], ntX);
|
||||
VectorBetweenVertices(vertices[2], vertices[1], ntY);
|
||||
VectorBetweenVertices(vertices[3], vertices[2], ntZ);
|
||||
|
||||
var x = ntX;
|
||||
var y = ntY;
|
||||
var z = ntZ;
|
||||
|
||||
// This was generated using Mathematica
|
||||
var nx = x[3] * (y[2] * z[1] - y[1] * z[2])
|
||||
+ x[2] * (y[1] * z[3] - y[3] * z[1])
|
||||
+ x[1] * (y[3] * z[2] - y[2] * z[3]);
|
||||
var ny = x[3] * (y[0] * z[2] - y[2] * z[0])
|
||||
+ x[2] * (y[3] * z[0] - y[0] * z[3])
|
||||
+ x[0] * (y[2] * z[3] - y[3] * z[2]);
|
||||
var nz = x[3] * (y[1] * z[0] - y[0] * z[1])
|
||||
+ x[1] * (y[0] * z[3] - y[3] * z[0])
|
||||
+ x[0] * (y[3] * z[1] - y[1] * z[3]);
|
||||
var nw = x[2] * (y[0] * z[1] - y[1] * z[0])
|
||||
+ x[1] * (y[2] * z[0] - y[0] * z[2])
|
||||
+ x[0] * (y[1] * z[2] - y[2] * z[1]);
|
||||
|
||||
var norm = Math.Sqrt(nx * nx + ny * ny + nz * nz + nw * nw);
|
||||
|
||||
var f = 1.0 / norm;
|
||||
normal[0] = f * nx;
|
||||
normal[1] = f * ny;
|
||||
normal[2] = f * nz;
|
||||
normal[3] = f * nw;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the normal vector nd.
|
||||
/// </summary>
|
||||
/// <param name="vertices">The vertices.</param>
|
||||
/// <param name="normal">The normal.</param>
|
||||
private void FindNormalVectorND(int[] vertices, double[] normal)
|
||||
{
|
||||
/* We need to solve the matrix A n = B where
|
||||
* - A contains coordinates of vertices as columns
|
||||
* - B is vector with all 1's. Really, it should be the distance of
|
||||
* the plane from the origin, but - since we're not worried about that
|
||||
* here and we will normalize the normal anyway - all 1's suffices.
|
||||
*/
|
||||
var iPiv = matrixPivots;
|
||||
var data = nDMatrix;
|
||||
var norm = 0.0;
|
||||
|
||||
// Solve determinants by replacing x-th column by all 1.
|
||||
for (var x = 0; x < Dimension; x++)
|
||||
{
|
||||
for (var i = 0; i < Dimension; i++)
|
||||
{
|
||||
var offset = vertices[i] * Dimension;
|
||||
for (var j = 0; j < Dimension; j++)
|
||||
{
|
||||
// maybe I got the i/j mixed up here regarding the representation Math.net uses...
|
||||
// ...but it does not matter since Det(A) = Det(Transpose(A)).
|
||||
data[Dimension * i + j] = j == x ? 1.0 : PositionData[offset + j];
|
||||
}
|
||||
}
|
||||
LUFactor(data, Dimension, iPiv, nDNormalHelperVector);
|
||||
var coord = 1.0;
|
||||
for (var i = 0; i < Dimension; i++)
|
||||
{
|
||||
if (iPiv[i] != i) coord *= -data[Dimension * i + i]; // the determinant sign changes on row swap.
|
||||
else coord *= data[Dimension * i + i];
|
||||
}
|
||||
normal[x] = coord;
|
||||
norm += coord * coord;
|
||||
}
|
||||
|
||||
// Normalize the result
|
||||
var f = 1.0 / Math.Sqrt(norm);
|
||||
for (var i = 0; i < normal.Length; i++) normal[i] *= f;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Simplex Volume
|
||||
/// <summary>
|
||||
/// Gets the simplex volume. Prior to having enough edge vectors, the method pads the remaining with all
|
||||
/// "other numbers". So, yes, this method is not really finding the volume. But a relative volume-like measure. It
|
||||
/// uses the magnitude of the determinant as the volume stand-in following the Cayley-Menger theorem.
|
||||
/// </summary>
|
||||
/// <param name="edgeVectors">The edge vectors.</param>
|
||||
/// <param name="lastIndex">The last index.</param>
|
||||
/// <param name="bigNumber">The big number.</param>
|
||||
/// <returns>System.Double.</returns>
|
||||
internal double GetSimplexVolume(double[][] edgeVectors, int lastIndex, double bigNumber)
|
||||
{
|
||||
var A = new double[Dimension * Dimension];
|
||||
var index = 0;
|
||||
for (int i = 0; i < Dimension; i++)
|
||||
for (int j = 0; j < Dimension; j++)
|
||||
if (i <= lastIndex)
|
||||
A[index++] = edgeVectors[i][j];
|
||||
else A[index] = (Math.Pow(-1, index) * index++) / bigNumber;
|
||||
// this last term is used for all the vertices in the comparison for the yet determined vertices
|
||||
// the idea is to come up with sets of numbers that are orthogonal so that an non-zero value will result
|
||||
// and to choose smallish numbers since the choice of vectors will affect what the end volume is.
|
||||
// A better way (todo?) is to solve a smaller matrix. However, cases were found in which the obvious smaller vector
|
||||
// (the upper left) had too many zeros. So, one would need to find the right subset. Indeed choosing a subset
|
||||
// biases the first dimensions of the others. Perhaps a larger volume would be created from a different vertex
|
||||
// if another subset of dimensions were used.
|
||||
return Math.Abs(DeterminantDestructive(A));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determinants the destructive.
|
||||
/// </summary>
|
||||
/// <param name="A">a.</param>
|
||||
/// <returns>System.Double.</returns>
|
||||
private double DeterminantDestructive(double[] A)
|
||||
{
|
||||
switch (Dimension)
|
||||
{
|
||||
case 0:
|
||||
return 0.0;
|
||||
case 1:
|
||||
return A[0];
|
||||
case 2:
|
||||
return A[0] * A[3] - A[1] * A[2];
|
||||
case 3:
|
||||
return A[0] * A[4] * A[8] + A[1] * A[5] * A[6] + A[2] * A[3] * A[7]
|
||||
- A[0] * A[5] * A[7] - A[1] * A[3] * A[8] - A[2] * A[4] * A[6];
|
||||
default:
|
||||
{
|
||||
var iPiv = new int[Dimension];
|
||||
var helper = new double[Dimension];
|
||||
LUFactor(A, Dimension, iPiv, helper);
|
||||
var det = 1.0;
|
||||
for (var i = 0; i < iPiv.Length; i++)
|
||||
{
|
||||
det *= A[Dimension * i + i];
|
||||
if (iPiv[i] != i) det *= -1; // the determinant sign changes on row swap.
|
||||
}
|
||||
return det;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
// Modified from Math.NET
|
||||
// Copyright (c) 2009-2013 Math.NET
|
||||
/// <summary>
|
||||
/// Lus the factor.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <param name="order">The order.</param>
|
||||
/// <param name="ipiv">The ipiv.</param>
|
||||
/// <param name="vecLUcolj">The vec l ucolj.</param>
|
||||
private static void LUFactor(double[] data, int order, int[] ipiv, double[] vecLUcolj)
|
||||
{
|
||||
// Initialize the pivot matrix to the identity permutation.
|
||||
for (var i = 0; i < order; i++)
|
||||
{
|
||||
ipiv[i] = i;
|
||||
}
|
||||
|
||||
// Outer loop.
|
||||
for (var j = 0; j < order; j++)
|
||||
{
|
||||
var indexj = j * order;
|
||||
var indexjj = indexj + j;
|
||||
|
||||
// Make a copy of the j-th column to localize references.
|
||||
for (var i = 0; i < order; i++)
|
||||
{
|
||||
vecLUcolj[i] = data[indexj + i];
|
||||
}
|
||||
|
||||
// Apply previous transformations.
|
||||
for (var i = 0; i < order; i++)
|
||||
{
|
||||
// Most of the time is spent in the following dot product.
|
||||
var kmax = Math.Min(i, j);
|
||||
var s = 0.0;
|
||||
for (var k = 0; k < kmax; k++)
|
||||
{
|
||||
s += data[k * order + i] * vecLUcolj[k];
|
||||
}
|
||||
|
||||
data[indexj + i] = vecLUcolj[i] -= s;
|
||||
}
|
||||
|
||||
// Find pivot and exchange if necessary.
|
||||
var p = j;
|
||||
for (var i = j + 1; i < order; i++)
|
||||
{
|
||||
if (Math.Abs(vecLUcolj[i]) > Math.Abs(vecLUcolj[p]))
|
||||
{
|
||||
p = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (p != j)
|
||||
{
|
||||
for (var k = 0; k < order; k++)
|
||||
{
|
||||
var indexk = k * order;
|
||||
var indexkp = indexk + p;
|
||||
var indexkj = indexk + j;
|
||||
var temp = data[indexkp];
|
||||
data[indexkp] = data[indexkj];
|
||||
data[indexkj] = temp;
|
||||
}
|
||||
|
||||
ipiv[j] = p;
|
||||
}
|
||||
|
||||
// Compute multipliers.
|
||||
if (j < order & data[indexjj] != 0.0)
|
||||
{
|
||||
for (var i = j + 1; i < order; i++)
|
||||
{
|
||||
data[indexj + i] /= data[indexjj];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8bb3327041efa18479319ec117493f8c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,218 +0,0 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
using System;
|
||||
|
||||
namespace MIConvexHull
|
||||
{
|
||||
/// <summary>
|
||||
/// A helper class for object allocation/storage.
|
||||
/// This helps the GC a lot as it prevents the creation of about 75% of
|
||||
/// new face objects (in the case of ConvexFaceInternal). In the case of
|
||||
/// FaceConnectors and DefferedFaces, the difference is even higher (in most
|
||||
/// cases O(1) vs O(number of created faces)).
|
||||
/// </summary>
|
||||
internal class ObjectManager
|
||||
{
|
||||
/// <summary>
|
||||
/// The dimension
|
||||
/// </summary>
|
||||
private readonly int Dimension;
|
||||
/// <summary>
|
||||
/// The connector stack
|
||||
/// </summary>
|
||||
private FaceConnector ConnectorStack;
|
||||
/// <summary>
|
||||
/// The deferred face stack
|
||||
/// </summary>
|
||||
private readonly SimpleList<DeferredFace> DeferredFaceStack;
|
||||
/// <summary>
|
||||
/// The empty buffer stack
|
||||
/// </summary>
|
||||
private readonly SimpleList<IndexBuffer> EmptyBufferStack;
|
||||
/// <summary>
|
||||
/// The face pool
|
||||
/// </summary>
|
||||
private ConvexFaceInternal[] FacePool;
|
||||
/// <summary>
|
||||
/// The face pool size
|
||||
/// </summary>
|
||||
private int FacePoolSize;
|
||||
/// <summary>
|
||||
/// The face pool capacity
|
||||
/// </summary>
|
||||
private int FacePoolCapacity;
|
||||
/// <summary>
|
||||
/// The free face indices
|
||||
/// </summary>
|
||||
private readonly IndexBuffer FreeFaceIndices;
|
||||
|
||||
/// <summary>
|
||||
/// The hull
|
||||
/// </summary>
|
||||
private readonly ConvexHullAlgorithm Hull;
|
||||
|
||||
/// <summary>
|
||||
/// Create the manager.
|
||||
/// </summary>
|
||||
/// <param name="hull">The hull.</param>
|
||||
public ObjectManager(ConvexHullAlgorithm hull)
|
||||
{
|
||||
Dimension = hull.NumOfDimensions;
|
||||
Hull = hull;
|
||||
FacePool = hull.FacePool;
|
||||
FacePoolSize = 0;
|
||||
FacePoolCapacity = hull.FacePool.Length;
|
||||
FreeFaceIndices = new IndexBuffer();
|
||||
|
||||
EmptyBufferStack = new SimpleList<IndexBuffer>();
|
||||
DeferredFaceStack = new SimpleList<DeferredFace>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the face to the pool for later use.
|
||||
/// </summary>
|
||||
/// <param name="faceIndex">Index of the face.</param>
|
||||
public void DepositFace(int faceIndex)
|
||||
{
|
||||
var face = FacePool[faceIndex];
|
||||
var af = face.AdjacentFaces;
|
||||
for (var i = 0; i < af.Length; i++)
|
||||
{
|
||||
af[i] = -1;
|
||||
}
|
||||
FreeFaceIndices.Push(faceIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reallocate the face pool, including the AffectedFaceFlags
|
||||
/// </summary>
|
||||
private void ReallocateFacePool()
|
||||
{
|
||||
var newPool = new ConvexFaceInternal[2*FacePoolCapacity];
|
||||
var newTags = new bool[2*FacePoolCapacity];
|
||||
Array.Copy(FacePool, newPool, FacePoolCapacity);
|
||||
Buffer.BlockCopy(Hull.AffectedFaceFlags, 0, newTags, 0, FacePoolCapacity*sizeof (bool));
|
||||
FacePoolCapacity = 2*FacePoolCapacity;
|
||||
Hull.FacePool = newPool;
|
||||
FacePool = newPool;
|
||||
Hull.AffectedFaceFlags = newTags;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new face and put it in the pool.
|
||||
/// </summary>
|
||||
/// <returns>System.Int32.</returns>
|
||||
private int CreateFace()
|
||||
{
|
||||
var index = FacePoolSize;
|
||||
var face = new ConvexFaceInternal(Dimension, index, GetVertexBuffer());
|
||||
FacePoolSize++;
|
||||
if (FacePoolSize > FacePoolCapacity) ReallocateFacePool();
|
||||
FacePool[index] = face;
|
||||
return index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return index of an unused face or creates a new one.
|
||||
/// </summary>
|
||||
/// <returns>System.Int32.</returns>
|
||||
public int GetFace()
|
||||
{
|
||||
if (FreeFaceIndices.Count > 0) return FreeFaceIndices.Pop();
|
||||
return CreateFace();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Store a face connector in the "embedded" linked list.
|
||||
/// </summary>
|
||||
/// <param name="connector">The connector.</param>
|
||||
public void DepositConnector(FaceConnector connector)
|
||||
{
|
||||
if (ConnectorStack == null)
|
||||
{
|
||||
connector.Next = null;
|
||||
ConnectorStack = connector;
|
||||
}
|
||||
else
|
||||
{
|
||||
connector.Next = ConnectorStack;
|
||||
ConnectorStack = connector;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get an unused face connector. If none is available, create it.
|
||||
/// </summary>
|
||||
/// <returns>FaceConnector.</returns>
|
||||
public FaceConnector GetConnector()
|
||||
{
|
||||
if (ConnectorStack == null) return new FaceConnector(Dimension);
|
||||
|
||||
var ret = ConnectorStack;
|
||||
ConnectorStack = ConnectorStack.Next;
|
||||
ret.Next = null;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deposit the index buffer.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer.</param>
|
||||
public void DepositVertexBuffer(IndexBuffer buffer)
|
||||
{
|
||||
buffer.Clear();
|
||||
EmptyBufferStack.Push(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a store index buffer or create a new instance.
|
||||
/// </summary>
|
||||
/// <returns>IndexBuffer.</returns>
|
||||
public IndexBuffer GetVertexBuffer()
|
||||
{
|
||||
return EmptyBufferStack.Count != 0 ? EmptyBufferStack.Pop() : new IndexBuffer();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deposit the deferred face.
|
||||
/// </summary>
|
||||
/// <param name="face">The face.</param>
|
||||
public void DepositDeferredFace(DeferredFace face)
|
||||
{
|
||||
DeferredFaceStack.Push(face);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the deferred face.
|
||||
/// </summary>
|
||||
/// <returns>DeferredFace.</returns>
|
||||
public DeferredFace GetDeferredFace()
|
||||
{
|
||||
return DeferredFaceStack.Count != 0 ? DeferredFaceStack.Pop() : new DeferredFace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e8d582e97c6824740ba9e1a41641b372
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using MIConvexHull;
|
||||
|
||||
public class Face3 : ConvexFace<Vertex3, Face3>
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 30a2635ffe51545499499ce392b234c8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
namespace MIConvexHull
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface for a structure with nD position.
|
||||
/// </summary>
|
||||
public interface IVertex
|
||||
{
|
||||
/// <summary>
|
||||
/// Position of the vertex.
|
||||
/// </summary>
|
||||
/// <value>The position.</value>
|
||||
double[] Position { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// "Default" vertex.
|
||||
/// </summary>
|
||||
/// <seealso cref="MIConvexHull.IVertex" />
|
||||
public class DefaultVertex : IVertex
|
||||
{
|
||||
/// <summary>
|
||||
/// Position of the vertex.
|
||||
/// </summary>
|
||||
/// <value>The position.</value>
|
||||
public double[] Position { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a55f713690988bb48aef7cbd4f01ebc7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,181 +0,0 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MIConvexHull
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple interface to unify different types of triangulations in the future.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex">The type of the t vertex.</typeparam>
|
||||
/// <typeparam name="TCell">The type of the t cell.</typeparam>
|
||||
public interface ITriangulation<TVertex, TCell>
|
||||
where TCell : TriangulationCell<TVertex, TCell>, new()
|
||||
where TVertex : IVertex
|
||||
{
|
||||
/// <summary>
|
||||
/// Triangulation simplexes. For 2D - triangles, 3D - tetrahedrons, etc ...
|
||||
/// </summary>
|
||||
/// <value>The cells.</value>
|
||||
IEnumerable<TCell> Cells { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Factory class for creating triangulations.
|
||||
/// </summary>
|
||||
public static class Triangulation
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates the Delaunay triangulation of the input data.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex">The type of the t vertex.</typeparam>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <param name="PlaneDistanceTolerance">The plane distance tolerance (default is 1e-10). If too high, points
|
||||
/// will be missed. If too low, the algorithm may break. Only adjust if you notice problems.</param>
|
||||
/// <returns>ITriangulation<TVertex, DefaultTriangulationCell<TVertex>>.</returns>
|
||||
public static ITriangulation<TVertex, DefaultTriangulationCell<TVertex>> CreateDelaunay<TVertex>(
|
||||
IList<TVertex> data,
|
||||
double PlaneDistanceTolerance = Constants.DefaultPlaneDistanceTolerance)
|
||||
where TVertex : IVertex
|
||||
{
|
||||
return DelaunayTriangulation<TVertex, DefaultTriangulationCell<TVertex>>.Create(data, PlaneDistanceTolerance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the Delaunay triangulation of the input data.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <param name="PlaneDistanceTolerance">The plane distance tolerance (default is 1e-10). If too high, points
|
||||
/// will be missed. If too low, the algorithm may break. Only adjust if you notice problems.</param>
|
||||
/// <returns>ITriangulation<DefaultVertex, DefaultTriangulationCell<DefaultVertex>>.</returns>
|
||||
public static ITriangulation<DefaultVertex, DefaultTriangulationCell<DefaultVertex>> CreateDelaunay(
|
||||
IList<double[]> data,
|
||||
double PlaneDistanceTolerance = Constants.DefaultPlaneDistanceTolerance)
|
||||
{
|
||||
var points = data.Select(p => new DefaultVertex { Position = p }).ToList();
|
||||
return DelaunayTriangulation<DefaultVertex, DefaultTriangulationCell<DefaultVertex>>.Create(points, PlaneDistanceTolerance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the Delaunay triangulation of the input data.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex">The type of the t vertex.</typeparam>
|
||||
/// <typeparam name="TFace">The type of the t face.</typeparam>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <param name="PlaneDistanceTolerance">The plane distance tolerance (default is 1e-10). If too high, points
|
||||
/// will be missed. If too low, the algorithm may break. Only adjust if you notice problems.</param>
|
||||
/// <returns>ITriangulation<TVertex, TFace>.</returns>
|
||||
public static ITriangulation<TVertex, TFace> CreateDelaunay<TVertex, TFace>(IList<TVertex> data,
|
||||
double PlaneDistanceTolerance = Constants.DefaultPlaneDistanceTolerance)
|
||||
where TVertex : IVertex
|
||||
where TFace : TriangulationCell<TVertex, TFace>, new()
|
||||
{
|
||||
return DelaunayTriangulation<TVertex, TFace>.Create(data, PlaneDistanceTolerance);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create the voronoi mesh.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex">The type of the t vertex.</typeparam>
|
||||
/// <typeparam name="TCell">The type of the t cell.</typeparam>
|
||||
/// <typeparam name="TEdge">The type of the t edge.</typeparam>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <param name="PlaneDistanceTolerance">The plane distance tolerance (default is 1e-10). If too high, points
|
||||
/// will be missed. If too low, the algorithm may break. Only adjust if you notice problems.</param>
|
||||
/// <returns>VoronoiMesh<TVertex, TCell, TEdge>.</returns>
|
||||
public static VoronoiMesh<TVertex, TCell, TEdge> CreateVoronoi<TVertex, TCell, TEdge>(IList<TVertex> data,
|
||||
double PlaneDistanceTolerance = Constants.DefaultPlaneDistanceTolerance)
|
||||
where TCell : TriangulationCell<TVertex, TCell>, new()
|
||||
where TVertex : IVertex
|
||||
where TEdge : VoronoiEdge<TVertex, TCell>, new()
|
||||
{
|
||||
return VoronoiMesh<TVertex, TCell, TEdge>.Create(data, PlaneDistanceTolerance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the voronoi mesh.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex">The type of the t vertex.</typeparam>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <param name="PlaneDistanceTolerance">The plane distance tolerance (default is 1e-10). If too high, points
|
||||
/// will be missed. If too low, the algorithm may break. Only adjust if you notice problems.</param>
|
||||
/// <returns>VoronoiMesh<TVertex, DefaultTriangulationCell<TVertex>, VoronoiEdge<TVertex, DefaultTriangulationCell<TVertex>>>.</returns>
|
||||
public static
|
||||
VoronoiMesh
|
||||
<TVertex, DefaultTriangulationCell<TVertex>, VoronoiEdge<TVertex, DefaultTriangulationCell<TVertex>>>
|
||||
CreateVoronoi<TVertex>(IList<TVertex> data,
|
||||
double PlaneDistanceTolerance = Constants.DefaultPlaneDistanceTolerance)
|
||||
where TVertex : IVertex
|
||||
{
|
||||
return
|
||||
VoronoiMesh
|
||||
<TVertex, DefaultTriangulationCell<TVertex>, VoronoiEdge<TVertex, DefaultTriangulationCell<TVertex>>
|
||||
>.Create(data, PlaneDistanceTolerance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the voronoi mesh.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <param name="PlaneDistanceTolerance">The plane distance tolerance (default is 1e-10). If too high, points
|
||||
/// will be missed. If too low, the algorithm may break. Only adjust if you notice problems.</param>
|
||||
/// <returns>VoronoiMesh<DefaultVertex, DefaultTriangulationCell<DefaultVertex>, VoronoiEdge<DefaultVertex, DefaultTriangulationCell<DefaultVertex>>>.</returns>
|
||||
public static
|
||||
VoronoiMesh
|
||||
<DefaultVertex, DefaultTriangulationCell<DefaultVertex>,
|
||||
VoronoiEdge<DefaultVertex, DefaultTriangulationCell<DefaultVertex>>>
|
||||
CreateVoronoi(IList<double[]> data,
|
||||
double PlaneDistanceTolerance = Constants.DefaultPlaneDistanceTolerance)
|
||||
{
|
||||
var points = data.Select(p => new DefaultVertex { Position = p.ToArray() }).ToList();
|
||||
return
|
||||
VoronoiMesh
|
||||
<DefaultVertex, DefaultTriangulationCell<DefaultVertex>,
|
||||
VoronoiEdge<DefaultVertex, DefaultTriangulationCell<DefaultVertex>>>.Create(points, PlaneDistanceTolerance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the voronoi mesh.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex">The type of the t vertex.</typeparam>
|
||||
/// <typeparam name="TCell">The type of the t cell.</typeparam>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <param name="PlaneDistanceTolerance">The plane distance tolerance (default is 1e-10). If too high, points
|
||||
/// will be missed. If too low, the algorithm may break. Only adjust if you notice problems.</param>
|
||||
/// <returns>VoronoiMesh<TVertex, TCell, VoronoiEdge<TVertex, TCell>>.</returns>
|
||||
public static VoronoiMesh<TVertex, TCell, VoronoiEdge<TVertex, TCell>> CreateVoronoi<TVertex, TCell>(
|
||||
IList<TVertex> data,
|
||||
double PlaneDistanceTolerance = Constants.DefaultPlaneDistanceTolerance)
|
||||
where TVertex : IVertex
|
||||
where TCell : TriangulationCell<TVertex, TCell>, new()
|
||||
{
|
||||
return VoronoiMesh<TVertex, TCell, VoronoiEdge<TVertex, TCell>>.Create(data, PlaneDistanceTolerance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0762bfe861ed97749a222fb1c217788a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5bf99a94223c6e1458c243cb68091852
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MIConvexHull
|
||||
{
|
||||
/*
|
||||
* Code here handles triangulation related stuff.
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Class ConvexHullAlgorithm.
|
||||
/// </summary>
|
||||
internal partial class ConvexHullAlgorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// Computes the Delaunay triangulation.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex">The type of the t vertex.</typeparam>
|
||||
/// <typeparam name="TCell">The type of the t cell.</typeparam>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <param name="PlaneDistanceTolerance">The plane distance tolerance.</param>
|
||||
/// <returns>TCell[].</returns>
|
||||
internal static TCell[] GetDelaunayTriangulation<TVertex, TCell>(IList<TVertex> data,
|
||||
double PlaneDistanceTolerance )
|
||||
where TCell : TriangulationCell<TVertex, TCell>, new()
|
||||
where TVertex : IVertex
|
||||
{
|
||||
var ch = new ConvexHullAlgorithm(data.Cast<IVertex>().ToArray(), true, PlaneDistanceTolerance);
|
||||
ch.GetConvexHull();
|
||||
ch.RemoveUpperFaces();
|
||||
return ch.GetConvexFaces<TVertex, TCell>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes up facing Tetrahedrons from the triangulation.
|
||||
/// </summary>
|
||||
private void RemoveUpperFaces()
|
||||
{
|
||||
var delaunayFaces = ConvexFaces;
|
||||
var dimension = NumOfDimensions - 1;
|
||||
|
||||
// Remove the "upper" faces
|
||||
for (var i = delaunayFaces.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var candidateIndex = delaunayFaces[i];
|
||||
var candidate = FacePool[candidateIndex];
|
||||
if (candidate.Normal[dimension] >= 0.0)
|
||||
{
|
||||
for (var fi = 0; fi < candidate.AdjacentFaces.Length; fi++)
|
||||
{
|
||||
var af = candidate.AdjacentFaces[fi];
|
||||
if (af >= 0)
|
||||
{
|
||||
var face = FacePool[af];
|
||||
for (var j = 0; j < face.AdjacentFaces.Length; j++)
|
||||
{
|
||||
if (face.AdjacentFaces[j] == candidateIndex)
|
||||
{
|
||||
face.AdjacentFaces[j] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delaunayFaces[i] = delaunayFaces[delaunayFaces.Count - 1];
|
||||
delaunayFaces.Pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: de8661d86f2667647b9dca59c1c97e9a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MIConvexHull
|
||||
{
|
||||
/// <summary>
|
||||
/// Calculation and representation of Delaunay triangulation.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex">The type of the t vertex.</typeparam>
|
||||
/// <typeparam name="TCell">The type of the t cell.</typeparam>
|
||||
/// <seealso cref="MIConvexHull.ITriangulation{TVertex, TCell}" />
|
||||
public class DelaunayTriangulation<TVertex, TCell> : ITriangulation<TVertex, TCell>
|
||||
where TCell : TriangulationCell<TVertex, TCell>, new()
|
||||
where TVertex : IVertex
|
||||
{
|
||||
/// <summary>
|
||||
/// Can only be created using a factory method.
|
||||
/// </summary>
|
||||
private DelaunayTriangulation()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cells of the triangulation.
|
||||
/// </summary>
|
||||
/// <value>The cells.</value>
|
||||
public IEnumerable<TCell> Cells { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates the Delaunay triangulation of the input data.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <param name="PlaneDistanceTolerance">The plane distance tolerance (default is 1e-10). If too high, points
|
||||
/// will be missed. If too low, the algorithm may break. Only adjust if you notice problems.</param>
|
||||
/// <returns>DelaunayTriangulation<TVertex, TCell>.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">data</exception>
|
||||
/// <exception cref="ArgumentNullException">data</exception>
|
||||
public static DelaunayTriangulation<TVertex, TCell> Create(IList<TVertex> data,
|
||||
double PlaneDistanceTolerance)// = Constants.DefaultPlaneDistanceTolerance)
|
||||
{
|
||||
if (data == null) throw new ArgumentNullException("data");
|
||||
if (data.Count == 0) return new DelaunayTriangulation<TVertex, TCell> { Cells = new TCell[0] };
|
||||
|
||||
var cells = ConvexHullAlgorithm.GetDelaunayTriangulation<TVertex, TCell>(data, PlaneDistanceTolerance);
|
||||
|
||||
return new DelaunayTriangulation<TVertex, TCell> { Cells = cells };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4b0e872e78d2ab8428b446cfa5c06580
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
namespace MIConvexHull
|
||||
{
|
||||
/// <summary>
|
||||
/// Representation of the triangulation cell. Pretty much the same as ConvexFace,
|
||||
/// just wanted to distinguish the two.
|
||||
/// To declare your own face type, use class Face : DelaunayFace(of Vertex, of Face)
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex">The type of the t vertex.</typeparam>
|
||||
/// <typeparam name="TCell">The type of the t cell.</typeparam>
|
||||
/// <seealso cref="MIConvexHull.ConvexFace{TVertex, TCell}" />
|
||||
public abstract class TriangulationCell<TVertex, TCell> : ConvexFace<TVertex, TCell>
|
||||
where TVertex : IVertex
|
||||
where TCell : ConvexFace<TVertex, TCell>
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default triangulation cell.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex">The type of the t vertex.</typeparam>
|
||||
public class DefaultTriangulationCell<TVertex> : TriangulationCell<TVertex, DefaultTriangulationCell<TVertex>>
|
||||
where TVertex : IVertex
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 808182249e0e66143b65945d9aeb37fe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
namespace MIConvexHull
|
||||
{
|
||||
/// <summary>
|
||||
/// A class representing an (undirected) edge of the Voronoi graph.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex">The type of the t vertex.</typeparam>
|
||||
/// <typeparam name="TCell">The type of the t cell.</typeparam>
|
||||
public class VoronoiEdge<TVertex, TCell>
|
||||
where TVertex : IVertex
|
||||
where TCell : TriangulationCell<TVertex, TCell>
|
||||
{
|
||||
/// <summary>
|
||||
/// Create an instance of the edge.
|
||||
/// </summary>
|
||||
public VoronoiEdge()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of the edge.
|
||||
/// </summary>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="target">The target.</param>
|
||||
public VoronoiEdge(TCell source, TCell target)
|
||||
{
|
||||
Source = source;
|
||||
Target = target;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source of the edge.
|
||||
/// </summary>
|
||||
/// <value>The source.</value>
|
||||
public TCell Source { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Target of the edge.
|
||||
/// </summary>
|
||||
/// <value>The target.</value>
|
||||
public TCell Target { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// ...
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to compare with the current object.</param>
|
||||
/// <returns><c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
var other = obj as VoronoiEdge<TVertex, TCell>;
|
||||
if (other == null) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return (Source == other.Source && Target == other.Target)
|
||||
|| (Source == other.Target && Target == other.Source);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ...
|
||||
/// </summary>
|
||||
/// <returns>A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hash = 23;
|
||||
hash = hash*31 + Source.GetHashCode();
|
||||
return hash*31 + Target.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2f5c388c78c121a47b14c2f4ed26af38
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,203 +0,0 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* MIConvexHull, Copyright (c) 2015 David Sehnal, Matthew Campbell
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MIConvexHull
|
||||
{
|
||||
/// <summary>
|
||||
/// A factory class for creating a Voronoi mesh.
|
||||
/// </summary>
|
||||
public static class VoronoiMesh
|
||||
{
|
||||
/// <summary>
|
||||
/// Create the voronoi mesh.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex">The type of the t vertex.</typeparam>
|
||||
/// <typeparam name="TCell">The type of the t cell.</typeparam>
|
||||
/// <typeparam name="TEdge">The type of the t edge.</typeparam>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <returns>VoronoiMesh<TVertex, TCell, TEdge>.</returns>
|
||||
public static VoronoiMesh<TVertex, TCell, TEdge> Create<TVertex, TCell, TEdge>(IList<TVertex> data,
|
||||
double PlaneDistanceTolerance = Constants.DefaultPlaneDistanceTolerance)
|
||||
where TCell : TriangulationCell<TVertex, TCell>, new()
|
||||
where TVertex : IVertex
|
||||
where TEdge : VoronoiEdge<TVertex, TCell>, new()
|
||||
{
|
||||
return VoronoiMesh<TVertex, TCell, TEdge>.Create(data, PlaneDistanceTolerance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the voronoi mesh.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex">The type of the t vertex.</typeparam>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <returns>VoronoiMesh<TVertex, DefaultTriangulationCell<TVertex>, VoronoiEdge<TVertex, DefaultTriangulationCell<TVertex>>>.</returns>
|
||||
public static
|
||||
VoronoiMesh
|
||||
<TVertex, DefaultTriangulationCell<TVertex>, VoronoiEdge<TVertex, DefaultTriangulationCell<TVertex>>>
|
||||
Create<TVertex>(IList<TVertex> data,
|
||||
double PlaneDistanceTolerance = Constants.DefaultPlaneDistanceTolerance)
|
||||
where TVertex : IVertex
|
||||
{
|
||||
return
|
||||
VoronoiMesh
|
||||
<TVertex, DefaultTriangulationCell<TVertex>, VoronoiEdge<TVertex, DefaultTriangulationCell<TVertex>>
|
||||
>.Create(data, PlaneDistanceTolerance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the voronoi mesh.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <returns>VoronoiMesh<DefaultVertex, DefaultTriangulationCell<DefaultVertex>, VoronoiEdge<DefaultVertex, DefaultTriangulationCell<DefaultVertex>>>.</returns>
|
||||
public static
|
||||
VoronoiMesh
|
||||
<DefaultVertex, DefaultTriangulationCell<DefaultVertex>,
|
||||
VoronoiEdge<DefaultVertex, DefaultTriangulationCell<DefaultVertex>>>
|
||||
Create(IList<double[]> data,
|
||||
double PlaneDistanceTolerance = Constants.DefaultPlaneDistanceTolerance)
|
||||
{
|
||||
var points = data.Select(p => new DefaultVertex { Position = p.ToArray() }).ToList();
|
||||
return
|
||||
VoronoiMesh
|
||||
<DefaultVertex, DefaultTriangulationCell<DefaultVertex>,
|
||||
VoronoiEdge<DefaultVertex, DefaultTriangulationCell<DefaultVertex>>>.Create(points, PlaneDistanceTolerance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the voronoi mesh.
|
||||
/// </summary>
|
||||
/// <typeparam name="TVertex">The type of the t vertex.</typeparam>
|
||||
/// <typeparam name="TCell">The type of the t cell.</typeparam>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <returns>VoronoiMesh<TVertex, TCell, VoronoiEdge<TVertex, TCell>>.</returns>
|
||||
public static VoronoiMesh<TVertex, TCell, VoronoiEdge<TVertex, TCell>> Create<TVertex, TCell>(
|
||||
IList<TVertex> data,
|
||||
double PlaneDistanceTolerance = Constants.DefaultPlaneDistanceTolerance)
|
||||
where TVertex : IVertex
|
||||
where TCell : TriangulationCell<TVertex, TCell>, new()
|
||||
{
|
||||
return VoronoiMesh<TVertex, TCell, VoronoiEdge<TVertex, TCell>>.Create(data, PlaneDistanceTolerance);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A representation of a voronoi mesh.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEdge">The type of the t edge.</typeparam>
|
||||
/// <typeparam name="TVertex">The type of the t vertex.</typeparam>
|
||||
/// <typeparam name="TCell">The type of the t cell.</typeparam>
|
||||
public class VoronoiMesh<TVertex, TCell, TEdge>
|
||||
where TCell : TriangulationCell<TVertex, TCell>, new()
|
||||
where TVertex : IVertex
|
||||
where TEdge : VoronoiEdge<TVertex, TCell>, new()
|
||||
{
|
||||
/// <summary>
|
||||
/// Can only be created using a factory method.
|
||||
/// </summary>
|
||||
private VoronoiMesh()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Vertices of the diagram.
|
||||
/// </summary>
|
||||
/// <value>The vertices.</value>
|
||||
public IEnumerable<TCell> Vertices { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Edges connecting the cells.
|
||||
/// The same information can be retrieved Cells' Adjacency.
|
||||
/// </summary>
|
||||
/// <value>The edges.</value>
|
||||
public IEnumerable<TEdge> Edges { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a Voronoi diagram of the input data.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <param name="PlaneDistanceTolerance">The plane distance tolerance (default is 1e-10). If too high, points
|
||||
/// will be missed. If too low, the algorithm may break. Only adjust if you notice problems.</param>
|
||||
/// <returns>VoronoiMesh<TVertex, TCell, TEdge>.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">data</exception>
|
||||
/// <exception cref="ArgumentNullException">data</exception>
|
||||
public static VoronoiMesh<TVertex, TCell, TEdge> Create(IList<TVertex> data,
|
||||
double PlaneDistanceTolerance = Constants.DefaultPlaneDistanceTolerance)
|
||||
{
|
||||
if (data == null) throw new ArgumentNullException("data");
|
||||
|
||||
var t = DelaunayTriangulation<TVertex, TCell>.Create(data, PlaneDistanceTolerance);
|
||||
var vertices = t.Cells.ToList();
|
||||
var edges = new HashSet<TEdge>(new EdgeComparer());
|
||||
|
||||
foreach (var f in vertices)
|
||||
{
|
||||
for (var i = 0; i < f.Adjacency.Length; i++)
|
||||
{
|
||||
var af = f.Adjacency[i];
|
||||
if (af != null) edges.Add(new TEdge { Source = f, Target = af });
|
||||
}
|
||||
}
|
||||
|
||||
return new VoronoiMesh<TVertex, TCell, TEdge>
|
||||
{
|
||||
Vertices = vertices,
|
||||
Edges = edges.ToList()
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is probably not needed, but might make things a tiny bit faster.
|
||||
/// </summary>
|
||||
/// <seealso cref="System.Collections.Generic.IEqualityComparer{TEdge}" />
|
||||
private class EdgeComparer : IEqualityComparer<TEdge>
|
||||
{
|
||||
/// <summary>
|
||||
/// Equals the specified x.
|
||||
/// </summary>
|
||||
/// <param name="x">The x.</param>
|
||||
/// <param name="y">The y.</param>
|
||||
/// <returns>System.Boolean.</returns>
|
||||
public bool Equals(TEdge x, TEdge y)
|
||||
{
|
||||
return (x.Source == y.Source && x.Target == y.Target) || (x.Source == y.Target && x.Target == y.Source);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for this instance.
|
||||
/// </summary>
|
||||
/// <param name="obj">The <see cref="T:System.Object" /> for which a hash code is to be returned.</param>
|
||||
/// <returns>A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.</returns>
|
||||
public int GetHashCode(TEdge obj)
|
||||
{
|
||||
return obj.Source.GetHashCode() ^ obj.Target.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fbb5750f20479844eb4e504302755db9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using MIConvexHull;
|
||||
|
||||
public class Vertex2 : IVertex
|
||||
{
|
||||
|
||||
public double[] Position => new[] {(double) StoredPosition.x, StoredPosition.y};
|
||||
|
||||
public Vector2 StoredPosition;
|
||||
|
||||
public float x { get { return StoredPosition.x; } }
|
||||
public float y { get { return StoredPosition.y; } }
|
||||
|
||||
public Vertex2(float x, float y)
|
||||
{
|
||||
StoredPosition = new Vector2(x, y);
|
||||
}
|
||||
|
||||
public Vertex2(Vector2 pos)
|
||||
{
|
||||
StoredPosition = pos;
|
||||
}
|
||||
|
||||
public Vector2 ToVector2() {
|
||||
return StoredPosition;
|
||||
}
|
||||
|
||||
public Vector3 ToVector3() {
|
||||
return StoredPosition;
|
||||
}
|
||||
|
||||
public double Distance(Vertex2 v)
|
||||
{
|
||||
return Math.Sqrt((v.x - x) * (v.x - x) + (v.y - y) * (v.y - y));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 67d3ec22ea52a2642a14ff864e3017b0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using MIConvexHull;
|
||||
|
||||
public class Vertex3 : IVertex
|
||||
{
|
||||
|
||||
public double[] Position { get; set; }
|
||||
|
||||
public double x { get { return Position[0]; } }
|
||||
public double y { get { return Position[1]; } }
|
||||
public double z { get { return Position[2]; } }
|
||||
|
||||
public Vertex3(double x, double y, double z)
|
||||
{
|
||||
Position = new double[] { x, y, z };
|
||||
}
|
||||
|
||||
public Vector3 ToVector3() {
|
||||
return new Vector3((float) Position[0], (float) Position[1], (float) Position[2]);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3553f231414f28347996a803a79425fa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a80bb5b7ce4059d40a0eddd80b4b4ae5
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'">
|
||||
<DefineConstants>PERSISTENCE;REPORTING</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<DefineConstants>PERSISTENCE;REPORTING;TEST_MSAGL</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="AutomaticGraphLayout.nuspec" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 40dcc7efbe87de246a2a5146c5128222
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
<package>
|
||||
<metadata>
|
||||
<id>AutomaticGraphLayout</id>
|
||||
<version>1.1.4</version>
|
||||
<authors>paulovila</authors>
|
||||
<owners>Microsoft</owners>
|
||||
<licenseUrl>https://github.com/Microsoft/automatic-graph-layout/blob/master/LICENSE</licenseUrl>
|
||||
<projectUrl>https://github.com/Microsoft/automatic-graph-layout</projectUrl>
|
||||
<requireLicenseAcceptance>true</requireLicenseAcceptance>
|
||||
<description>This .NET asssembly contains the core MSAGL layout functionality. Use this library if you just want MSAGL to perform the layout only and afterwards you will use a separate tool to perform the rendering and visalization.</description>
|
||||
<releaseNotes>
|
||||
</releaseNotes>
|
||||
<copyright>Copyright © Microsoft Corporation</copyright>
|
||||
<language>en-US</language>
|
||||
<tags>MSAGL Graph Layout</tags>
|
||||
<dependencies>
|
||||
</dependencies>
|
||||
<frameworkAssemblies>
|
||||
</frameworkAssemblies>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="./bin/release/netstandard2.0/AutomaticGraphLayout.dll" target="lib/netstandard2.0" />
|
||||
</files>
|
||||
</package>
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 64a9bda5ccebb9e45b4c45a2f2d7d9da
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f3f9e18906f0e744eb7e5b695467d1cc
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,305 +0,0 @@
|
|||
using System;
|
||||
namespace Microsoft.Msagl.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for algorithms that are cancelable and report progress
|
||||
/// </summary>
|
||||
public abstract class AlgorithmBase
|
||||
{
|
||||
#region Running
|
||||
/// <summary>
|
||||
/// Runs the algorithm.
|
||||
/// Inherits any preexisting cancel tokens.
|
||||
/// </summary>
|
||||
public void Run()
|
||||
{
|
||||
this.progressRatio = 0;
|
||||
this.localStepCount = 0;
|
||||
|
||||
this.cancelToken = threadStaticCancelToken;
|
||||
this.ThrowIfCanceled();
|
||||
|
||||
this.ProgressSteps(0); // Initial progress
|
||||
|
||||
this.RunInternal();
|
||||
|
||||
if (!this.IsCanceled)
|
||||
{
|
||||
this.ProgressComplete();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the algorithm, setting up the cancel token and reverting to the old cancel token before finishing
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "cancelToken")]
|
||||
public void Run(CancelToken cancelToken)
|
||||
{
|
||||
this.progressRatio = 0;
|
||||
this.localStepCount = 0;
|
||||
|
||||
CancelToken oldCancelToken = this.SetCancelToken(cancelToken);
|
||||
|
||||
try
|
||||
{
|
||||
this.ProgressSteps(0); // Initial progress
|
||||
|
||||
this.RunInternal();
|
||||
|
||||
if (!this.IsCanceled)
|
||||
{
|
||||
this.ProgressComplete();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
threadStaticCancelToken = oldCancelToken;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the actual algorithm.
|
||||
/// </summary>
|
||||
protected abstract void RunInternal();
|
||||
|
||||
#endregion Running
|
||||
|
||||
#region Cancel
|
||||
|
||||
/// <summary>
|
||||
/// Set the cancel token. Returns the old cancel token, in case you need to revert at some stage.
|
||||
/// </summary>
|
||||
/// <param name="cancelToken">new cancel token</param>
|
||||
/// <returns>old cancel token</returns>
|
||||
public CancelToken SetCancelToken(CancelToken cancelToken)
|
||||
{
|
||||
this.cancelToken = cancelToken;
|
||||
this.ThrowIfCanceled();
|
||||
CancelToken oldCancelToken = threadStaticCancelToken;
|
||||
threadStaticCancelToken = cancelToken;
|
||||
return oldCancelToken;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancels the algorithm that is currently in progress.
|
||||
/// </summary>
|
||||
public void Cancel()
|
||||
{
|
||||
if (this.cancelToken != null)
|
||||
{
|
||||
this.cancelToken.Canceled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if the algorithm has been canceled.
|
||||
/// </summary>
|
||||
public bool IsCanceled
|
||||
{
|
||||
get { return this.cancelToken != null && this.cancelToken.Canceled; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws an OperationCanceledException if the current algorithm has been canceled.
|
||||
/// </summary>
|
||||
protected void ThrowIfCanceled()
|
||||
{
|
||||
if (this.cancelToken != null)
|
||||
{
|
||||
this.cancelToken.ThrowIfCanceled();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The current cancel token - one per thread, cached by cancelToken, below.
|
||||
/// </summary>
|
||||
[ThreadStatic]
|
||||
private static CancelToken threadStaticCancelToken;
|
||||
|
||||
/// <summary>
|
||||
/// ThreadStatic field above has about 10X the lookup cost of a local field, so we
|
||||
/// cache its value in cancelToken
|
||||
/// </summary>
|
||||
private volatile CancelToken cancelToken;
|
||||
|
||||
/// <summary>
|
||||
/// the current cancel token
|
||||
/// </summary>
|
||||
protected CancelToken CancelToken { get { return cancelToken; } }
|
||||
|
||||
#endregion Cancel
|
||||
|
||||
#region Progress
|
||||
|
||||
/// <summary>
|
||||
/// notifies whenever progress is made by the algorithm. ProgressChangedEventArgs report
|
||||
/// 0 < progressRatio <= 1
|
||||
/// </summary>
|
||||
public event EventHandler<ProgressChangedEventArgs> ProgressChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Total progress to date
|
||||
/// </summary>
|
||||
private double progressRatio = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Step size to increment progressRatio when ProgressStep is called
|
||||
/// </summary>
|
||||
private double localProgressStepSize = LocalProgressStepSizeDefault;
|
||||
|
||||
/// <summary>
|
||||
/// True if the progress step count has been specified using StartListenToLocalProgress.
|
||||
/// </summary>
|
||||
private bool localProgressSpecified = false;
|
||||
|
||||
/// <summary>
|
||||
/// The number of times ProgressStep has been called.
|
||||
/// </summary>
|
||||
private int localStepCount = 0;
|
||||
|
||||
/// <summary>
|
||||
/// When running sub-algorithms this gives the progressRatio start point
|
||||
/// </summary>
|
||||
private double stageStartRatio = 0;
|
||||
|
||||
/// <summary>
|
||||
/// When running sub-algorithms this gives the progressRatio end point
|
||||
/// </summary>
|
||||
private double stageEndRatio = 1;
|
||||
|
||||
/// <summary>
|
||||
/// if StartListenToLocalProgress is not used to setup step size the following will be the
|
||||
/// (rather arbitrary) default.
|
||||
/// </summary>
|
||||
private const double LocalProgressStepSizeDefault = 0.05;
|
||||
|
||||
/// <summary>
|
||||
/// setup to start listening to ProgressStep() invocations from this algorithm.
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
|
||||
protected void StartListenToLocalProgress(int expectedSteps, double stageRatio = 1)
|
||||
{
|
||||
localProgressStepSize = stageRatio / Math.Max(1, expectedSteps); // at least 1, to prevent divide by 0
|
||||
localProgressSpecified = true;
|
||||
localStepCount = 0;
|
||||
stageStartRatio = 0;
|
||||
stageEndRatio = 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When starting a sub-algorithm start listening to its progress, transforming
|
||||
/// the progressRatio it reports (between 0 and 1) to between progressRatio and progressRatio+stageRatio
|
||||
/// </summary>
|
||||
/// <param name="childAlgorithm">The child algorithm.</param>
|
||||
/// <param name="stageRatio">The fraction of time the child algorithm will take of the parent algorithm.</param>
|
||||
protected void StartListenToProgress(AlgorithmBase childAlgorithm, double stageRatio)
|
||||
{
|
||||
ValidateArg.IsNotNull(childAlgorithm, "childAlgorithm");
|
||||
childAlgorithm.ProgressChanged += NotifyProgressChanged;
|
||||
stageStartRatio = progressRatio;
|
||||
stageEndRatio = progressRatio + stageRatio;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When the sub-algorithm is completed this reverts us to listening to local progress
|
||||
/// </summary>
|
||||
/// <param name="childAlgorithm"></param>
|
||||
protected void StopListenToProgress(AlgorithmBase childAlgorithm)
|
||||
{
|
||||
ValidateArg.IsNotNull(childAlgorithm, "childAlgorithm");
|
||||
childAlgorithm.ProgressChanged -= NotifyProgressChanged;
|
||||
progressRatio = stageEndRatio;
|
||||
stageStartRatio = 0;
|
||||
stageEndRatio = 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the child algorithm and listens for progress changes.
|
||||
/// </summary>
|
||||
protected void RunChildAlgorithm(AlgorithmBase childAlgorithm, double stageRatio)
|
||||
{
|
||||
ValidateArg.IsNotNull(childAlgorithm, "childAlgorithm");
|
||||
try
|
||||
{
|
||||
StartListenToProgress(childAlgorithm, stageRatio);
|
||||
childAlgorithm.Run();
|
||||
}
|
||||
finally
|
||||
{
|
||||
StopListenToProgress(childAlgorithm);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call this to report 100% progress if your algorithm finishes early
|
||||
/// </summary>
|
||||
protected void ProgressComplete()
|
||||
{
|
||||
ThrowIfCanceled();
|
||||
if (progressRatio != 1.0)
|
||||
{
|
||||
this.progressRatio = 1.0;
|
||||
this.NotifyProgressChanged(this, new ProgressChangedEventArgs(progressRatio));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks for cancel, increases progress, and notifies any listeners of progress.
|
||||
/// Call whenever the algorithm makes progress.
|
||||
/// </summary>
|
||||
protected void ProgressStep()
|
||||
{
|
||||
this.ProgressSteps(1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks for cancel, increases progress, and notifies any listeners of progress.
|
||||
/// Call whenever the algorithm makes progress.
|
||||
/// </summary>
|
||||
protected void ProgressSteps(int stepsTaken)
|
||||
{
|
||||
ThrowIfCanceled();
|
||||
localStepCount += stepsTaken;
|
||||
|
||||
if (localProgressSpecified)
|
||||
{
|
||||
progressRatio = progressRatio + (localProgressStepSize * stepsTaken);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If no local step count was specified, use a progress bar
|
||||
// which never reaches 100% and gets slower as time goes on.
|
||||
// It approaches 85% (instead of 100%) so it never appears to be completed.
|
||||
const double Limit = 0.85;
|
||||
const double HalfLife = 50.0; // 50 was chosen as the default since it represents one ProgressStep per percentage at the start (i.e. 50 steps reach 50%)
|
||||
const double Numerator = Limit * HalfLife;
|
||||
progressRatio = Limit - (Numerator / (HalfLife + localStepCount));
|
||||
}
|
||||
|
||||
// Round up to 100% if the sum of steps doesn't quite reach it.
|
||||
if (Math.Round(progressRatio, 6) == 1.0)
|
||||
{
|
||||
progressRatio = 1.0;
|
||||
}
|
||||
|
||||
this.NotifyProgressChanged(this, new ProgressChangedEventArgs(progressRatio));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// notifies whenever progress is made by the algorithm. ProgressChangedEventArgs report
|
||||
/// 0 < progressRatio <= 1. Progress from sub-algorithms are converted to the correct ratio
|
||||
/// of the local progress
|
||||
/// </summary>
|
||||
private void NotifyProgressChanged(object sender, ProgressChangedEventArgs args)
|
||||
{
|
||||
if (ProgressChanged != null)
|
||||
{
|
||||
double stageRatio = stageEndRatio - stageStartRatio;
|
||||
double stageProgress = stageRatio * args.RatioComplete;
|
||||
ProgressChanged(this, new ProgressChangedEventArgs(stageStartRatio + stageProgress));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Progress Logic
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8f0e154226de7c44d93b02694d82946a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
using System;
|
||||
|
||||
#if PPC
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
#endif
|
||||
|
||||
namespace Microsoft.Msagl.Core {
|
||||
|
||||
/// <summary>
|
||||
/// a place holder for the cancelled flag
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable")]
|
||||
public class CancelToken {
|
||||
|
||||
volatile bool canceled;
|
||||
|
||||
/// <summary>
|
||||
/// Set this flag to true when you want to cancel the layout.
|
||||
/// </summary>
|
||||
public bool Canceled {
|
||||
get { return canceled; }
|
||||
set {
|
||||
canceled = value;
|
||||
#if PPC
|
||||
if (canceled)
|
||||
{
|
||||
cancellationTokenSource.Cancel();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// throws is the layout has been cancelled
|
||||
/// </summary>
|
||||
public void ThrowIfCanceled()
|
||||
{
|
||||
#if PPC
|
||||
cancellationTokenSource.Token.ThrowIfCancellationRequested();
|
||||
#endif
|
||||
if (this.Canceled)
|
||||
throw new OperationCanceledException();
|
||||
}
|
||||
|
||||
#if PPC
|
||||
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
/// <summary>
|
||||
/// Pass this cancelation token for use in cancellable parallel for-each operations
|
||||
/// </summary>
|
||||
public CancellationToken CancellationToken
|
||||
{
|
||||
get { return cancellationTokenSource.Token; }
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 838ad0009928e604e94e5580a15ada26
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 500f7d7386669de4abbd734ecd9ae3c3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,130 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace Microsoft.Msagl.Core.DataStructures {
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A priority queue based on the binary heap algorithm
|
||||
/// </summary>
|
||||
internal class BinaryHeapPriorityQueue {
|
||||
//indexing for A starts from 1
|
||||
|
||||
readonly int[] _heap;//array of heap elements
|
||||
readonly int[] _reverse_heap; // the map from [0,..., n-1] to their places in heap
|
||||
/// <summary>
|
||||
/// the array of priorities
|
||||
/// </summary>
|
||||
readonly double[] _priors;
|
||||
internal int Count { get { return heapSize; } }
|
||||
int heapSize;
|
||||
/// <summary>
|
||||
/// the constructor
|
||||
/// we suppose that all integers inserted into the queue will be less then n
|
||||
/// </summary>
|
||||
/// <param name="n">it is the number of different integers that will be inserted into the queue </param>
|
||||
internal BinaryHeapPriorityQueue(int n) {
|
||||
_priors = new double[n];
|
||||
_heap = new int[n + 1];//because indexing for A starts from 1
|
||||
_reverse_heap = new int[n];
|
||||
}
|
||||
|
||||
|
||||
void SwapWithParent(int i) {
|
||||
int parent = _heap[i >> 1];
|
||||
PutAtI(i >> 1, _heap[i]);
|
||||
PutAtI(i, parent);
|
||||
}
|
||||
|
||||
internal void Enqueue(int o, double priority) {
|
||||
heapSize++;
|
||||
int i = heapSize;
|
||||
_priors[o] = priority;
|
||||
PutAtI(i, o);
|
||||
while (i > 1 && _priors[_heap[i >> 1]] > priority) {
|
||||
SwapWithParent(i);
|
||||
i >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
void PutAtI(int i, int h) {
|
||||
_heap[i] = h;
|
||||
_reverse_heap[h] = i;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// return the first element of the queue and removes it from the queue
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal int Dequeue() {
|
||||
if (heapSize == 0)
|
||||
throw new InvalidOperationException();
|
||||
int ret = _heap[1];
|
||||
if (heapSize > 1)
|
||||
{
|
||||
PutAtI(1, _heap[heapSize]);
|
||||
int i = 1;
|
||||
while (true)
|
||||
{
|
||||
int smallest = i;
|
||||
int l = i << 1;
|
||||
|
||||
if (l <= heapSize && _priors[_heap[l]] <_priors[ _heap[i]])
|
||||
smallest = l;
|
||||
|
||||
int r = l + 1;
|
||||
|
||||
if (r <= heapSize && _priors[_heap[r]] < _priors[_heap[smallest]])
|
||||
smallest = r;
|
||||
|
||||
if (smallest != i)
|
||||
SwapWithParent(smallest);
|
||||
else
|
||||
break;
|
||||
|
||||
i = smallest;
|
||||
|
||||
}
|
||||
}
|
||||
heapSize--;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// sets the object priority to c
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal void DecreasePriority(int o, double newPriority) {
|
||||
|
||||
//System.Diagnostics.Debug.WriteLine("delcrease "+ o.ToString()+" to "+ newPriority.ToString());
|
||||
|
||||
_priors[o] = newPriority;
|
||||
int i = _reverse_heap[o];
|
||||
while (i > 1) {
|
||||
if (_priors[_heap[i]] < _priors[_heap[i >> 1]])
|
||||
SwapWithParent(i);
|
||||
else
|
||||
break;
|
||||
i >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
public static void Test() {
|
||||
var q = new BinaryHeapPriorityQueue(10);
|
||||
q.Enqueue(2, 2);
|
||||
q.Enqueue(1, 1);
|
||||
q.Enqueue(9, 9);
|
||||
q.Enqueue(8, 8);
|
||||
q.Enqueue(5, 5);
|
||||
q.Enqueue(3, 3);
|
||||
q.Enqueue(4, 4);
|
||||
q.Enqueue(7, 7);
|
||||
q.Enqueue(6, 6);
|
||||
q.Enqueue(0, 0);
|
||||
for (int i = 0; i < 10; i++)
|
||||
Console.WriteLine(q.Dequeue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a62428fef9112304eb0439efc749dc37
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Microsoft.Msagl.Core.DataStructures {
|
||||
/// <summary>
|
||||
/// A priority queue based on the binary heap algorithm.
|
||||
/// This class needs a comparer object to compare elements of the queue.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
internal class BinaryHeapWithComparer<T> {
|
||||
const int initialHeapCapacity = 16;
|
||||
T[] A; //array of the heap elems starting at A[1]
|
||||
|
||||
int heapSize = 0;
|
||||
|
||||
internal void Enqueue(T element) {
|
||||
if (heapSize == A.Length - 1) {
|
||||
var newA = new T[A.Length*2];
|
||||
Array.Copy(A, 1, newA, 1, heapSize);
|
||||
A = newA;
|
||||
}
|
||||
|
||||
int i = heapSize + 1;
|
||||
A[i] = element;
|
||||
heapSize++;
|
||||
int j = i >> 1;
|
||||
T parent, son;
|
||||
while (i > 1 && Less(son = A[i], parent = A[j])) {
|
||||
A[j] = son;
|
||||
A[i] = parent;
|
||||
i = j;
|
||||
j = i >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal T Dequeue() {
|
||||
if (heapSize < 1)
|
||||
throw new InvalidOperationException();
|
||||
T ret = A[1];
|
||||
|
||||
T candidate = A[heapSize];
|
||||
|
||||
heapSize--;
|
||||
|
||||
ChangeMinimum(candidate);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal void ChangeMinimum(T candidate) {
|
||||
A[1] = candidate;
|
||||
|
||||
int j = 1;
|
||||
int i = 2;
|
||||
bool done = false;
|
||||
while (i < heapSize && !done) {
|
||||
done = true;
|
||||
//both sons exist
|
||||
T leftSon = A[i];
|
||||
T rigthSon = A[i + 1];
|
||||
int compareResult = comparer.Compare(leftSon, rigthSon);
|
||||
if (compareResult < 0) {
|
||||
//left son is the smallest
|
||||
if (comparer.Compare(leftSon, candidate) < 0) {
|
||||
A[j] = leftSon;
|
||||
A[i] = candidate;
|
||||
done = false;
|
||||
j = i;
|
||||
i = j << 1;
|
||||
}
|
||||
} else {
|
||||
//right son in not the greatest
|
||||
if (comparer.Compare(rigthSon, candidate) < 0) {
|
||||
A[j] = rigthSon;
|
||||
A[i + 1] = candidate;
|
||||
done = false;
|
||||
j = i + 1;
|
||||
i = j << 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i == heapSize) {
|
||||
//can we do one more step:
|
||||
T leftSon = A[i];
|
||||
if (comparer.Compare(leftSon, candidate) < 0) {
|
||||
A[j] = leftSon;
|
||||
A[i] = candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal int Count {
|
||||
get { return heapSize; }
|
||||
}
|
||||
|
||||
internal bool Less(T a, T b) {
|
||||
return comparer.Compare(a, b) < 0;
|
||||
}
|
||||
|
||||
IComparer<T> comparer;
|
||||
|
||||
internal BinaryHeapWithComparer(IComparer<T> comparer) {
|
||||
A = new T[initialHeapCapacity + 1];
|
||||
this.comparer = comparer;
|
||||
}
|
||||
|
||||
#if TEST_MSAGL
|
||||
|
||||
public override string ToString() {
|
||||
int i = 1;
|
||||
return "{" + Print(i) + "}";
|
||||
}
|
||||
|
||||
string Print(int i) {
|
||||
if (2*i + 1 <= heapSize)
|
||||
return String.Format(CultureInfo.InvariantCulture, "({0}->{1},{2})", A[i], A[i*2], A[i*2 + 1]) +
|
||||
Print(i*2) + Print(i*2 + 1);
|
||||
if (2*i == heapSize)
|
||||
return String.Format(CultureInfo.InvariantCulture, "({0}->{1}", A[i], A[i*2]);
|
||||
if (i == heapSize && i == 1)
|
||||
return " " + A[i].ToString() + " ";
|
||||
return "";
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
public T GetMinimum() {
|
||||
return A[1];
|
||||
}
|
||||
|
||||
#if TEST_MSAGL
|
||||
//internal void UpdateMinimum() {
|
||||
// throw new NotImplementedException();
|
||||
//}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 78dc14a0e35a20145a57e0314da9ab9b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.Msagl.Core.DataStructures {
|
||||
/// <summary>
|
||||
/// various utilities for collections
|
||||
/// </summary>
|
||||
public class CollectionUtilities {
|
||||
/// <summary>
|
||||
/// Add value to dictionary
|
||||
/// </summary>
|
||||
internal static void AddToMap<TS, T, TC>(Dictionary<T, TC> dictionary, T key, TS value)
|
||||
where TC : ICollection<TS>, new() {
|
||||
TC tc;
|
||||
if (!dictionary.TryGetValue(key, out tc))
|
||||
dictionary[key] = tc = new TC();
|
||||
|
||||
tc.Add(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove value from dictionary
|
||||
/// </summary>
|
||||
[SuppressMessage("Microsoft.Naming", "CA1715:IdentifiersShouldHaveCorrectPrefix", MessageId = "T"),
|
||||
SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "S"),
|
||||
SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
|
||||
internal static void RemoveFromMap<TS, T, TC>(Dictionary<T, TC> dictionary, T key, TS value)
|
||||
where TC : ICollection<TS> {
|
||||
var tc = dictionary[key];
|
||||
tc.Remove(value);
|
||||
if (tc.Count == 0)
|
||||
dictionary.Remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6e8b582a3d0c32a41bc4eb7f0db98f96
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,281 +0,0 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Msagl.Core.DataStructures {
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A generic version priority queue based on the binary heap algorithm where
|
||||
/// the priority of each element is passed as a parameter.
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
|
||||
public class GenericBinaryHeapPriorityQueue<T> : IEnumerable<T> {
|
||||
|
||||
const int InitialHeapCapacity = 16;
|
||||
|
||||
|
||||
//indexing for A starts from 1
|
||||
|
||||
//[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
//internal void Clear()
|
||||
//{
|
||||
// if(heapSize>0)
|
||||
// {
|
||||
// for(int i=0;i<cache.Length;i++)
|
||||
// this.cache[i]=null;
|
||||
|
||||
// heapSize=0;
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
GenericHeapElement<T>[] A;//array of heap elements
|
||||
// ReSharper restore InconsistentNaming
|
||||
|
||||
/// <summary>
|
||||
/// it is a mapping from queue elements and their correspondent HeapElements
|
||||
/// </summary>
|
||||
readonly Dictionary<T, GenericHeapElement<T>> cache;
|
||||
internal int Count { get { return heapSize; } }
|
||||
int heapSize;
|
||||
|
||||
internal bool ContainsElement(T key) {
|
||||
return cache.ContainsKey(key);
|
||||
}
|
||||
|
||||
|
||||
internal GenericBinaryHeapPriorityQueue() {
|
||||
cache = new Dictionary<T, GenericHeapElement<T>>();
|
||||
A = new GenericHeapElement<T>[InitialHeapCapacity + 1];
|
||||
}
|
||||
|
||||
|
||||
void SwapWithParent(int i) {
|
||||
var parent = A[i >> 1];
|
||||
|
||||
PutAtI(i >> 1, A[i]);
|
||||
PutAtI(i, parent);
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal void Enqueue(T element, double priority) {
|
||||
if (heapSize == A.Length - 1) {
|
||||
var newA = new GenericHeapElement<T>[A.Length * 2];
|
||||
Array.Copy(A, 1, newA, 1, heapSize);
|
||||
A = newA;
|
||||
}
|
||||
|
||||
heapSize++;
|
||||
int i = heapSize;
|
||||
GenericHeapElement<T> h;
|
||||
|
||||
A[i] = cache[element] = h = new GenericHeapElement<T>(i, priority, element);
|
||||
while (i > 1 && A[i >> 1].priority.CompareTo(priority) > 0) {
|
||||
SwapWithParent(i);
|
||||
i >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool IsEmpty() {
|
||||
return heapSize == 0;
|
||||
}
|
||||
|
||||
void PutAtI(int i, GenericHeapElement<T> h) {
|
||||
A[i] = h;
|
||||
h.indexToA = i;
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// return the first element of the queue without removing it
|
||||
///// </summary>
|
||||
///// <returns></returns>
|
||||
//[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
//internal int Peek() {
|
||||
// return A[1].v;
|
||||
//}
|
||||
|
||||
internal T Dequeue() {
|
||||
if (heapSize == 0)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
var ret = A[1].v;
|
||||
|
||||
MoveQueueOneStepForward(ret);
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
internal T Dequeue(out double priority) {
|
||||
if (heapSize == 0) throw new InvalidOperationException();
|
||||
|
||||
var ret = A[1].v;
|
||||
priority = A[1].priority;
|
||||
MoveQueueOneStepForward(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void MoveQueueOneStepForward(T ret) {
|
||||
cache.Remove(ret);
|
||||
PutAtI(1, A[heapSize]);
|
||||
int i = 1;
|
||||
while (true) {
|
||||
int smallest = i;
|
||||
int l = i << 1;
|
||||
|
||||
if (l <= heapSize && A[l].priority.CompareTo(A[i].priority) < 0)
|
||||
smallest = l;
|
||||
|
||||
int r = l + 1;
|
||||
|
||||
if (r <= heapSize && A[r].priority.CompareTo(A[smallest].priority) < 0)
|
||||
smallest = r;
|
||||
|
||||
if (smallest != i)
|
||||
SwapWithParent(smallest);
|
||||
else
|
||||
break;
|
||||
|
||||
i = smallest;
|
||||
|
||||
}
|
||||
|
||||
heapSize--;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// sets the object priority to c
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal void DecreasePriority(T element, double newPriority)
|
||||
{
|
||||
GenericHeapElement<T> h;
|
||||
//ignore the element if it is not in the queue
|
||||
if (!cache.TryGetValue(element, out h)) return;
|
||||
|
||||
//var h = cache[element];
|
||||
h.priority = newPriority;
|
||||
int i = h.indexToA;
|
||||
while (i > 1) {
|
||||
if (A[i].priority.CompareTo(A[i >> 1].priority) < 0)
|
||||
SwapWithParent(i);
|
||||
else
|
||||
break;
|
||||
i >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
///<summary>
|
||||
///</summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
// public static void Test() {
|
||||
// Timer nm = new Timer();
|
||||
// var bound = 1000000;
|
||||
// nm.Start();
|
||||
// {
|
||||
// var q = new BinaryHeapPriorityQueue(bound + 1);
|
||||
// Random random = new Random();
|
||||
// for (int i = 0; i < bound; i++) {
|
||||
// q.Enqueue(i, random.NextDouble());
|
||||
// }
|
||||
// for (int i = 0; i < bound; i++) {
|
||||
// q.DecreasePriority(i, -random.NextDouble());
|
||||
// }
|
||||
//
|
||||
// while (q.IsEmpty() == false) {
|
||||
// q.Dequeue();
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// nm.Stop();
|
||||
// Console.WriteLine(nm.Duration);
|
||||
// nm.Start();
|
||||
// {
|
||||
// var q = new GenericBinaryHeapPriorityQueue<int>();
|
||||
// Random random = new Random();
|
||||
// for (int i = 0; i < bound; i++) {
|
||||
// q.Enqueue(i, random.NextDouble());
|
||||
// }
|
||||
// for (int i = 0; i < bound; i++) {
|
||||
// q.DecreasePriority(i, -random.NextDouble());
|
||||
// }
|
||||
//
|
||||
// while (q.IsEmpty() == false) {
|
||||
// q.Dequeue();
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// nm.Stop();
|
||||
// Console.WriteLine(nm.Duration);
|
||||
//}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
public static void Test() {
|
||||
var q = new GenericBinaryHeapPriorityQueue<int>();
|
||||
q.Enqueue(2, 2);
|
||||
q.Enqueue(1, 1);
|
||||
q.Enqueue(9, 9);
|
||||
q.Enqueue(8, 8);
|
||||
q.Enqueue(5, 5);
|
||||
q.Enqueue(3, 3);
|
||||
q.Enqueue(4, 4);
|
||||
q.Enqueue(7, 7);
|
||||
q.Enqueue(6, 6);
|
||||
q.Enqueue(0, 0);
|
||||
|
||||
q.DecreasePriority(4, 2.5);
|
||||
|
||||
while (q.IsEmpty() == false)
|
||||
Console.WriteLine(q.Dequeue());
|
||||
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// enumerator
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IEnumerator<T> GetEnumerator() {
|
||||
for (int i = 1; i <= heapSize; i++)
|
||||
yield return A[i].v;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="priority"></param>
|
||||
/// <returns></returns>
|
||||
public T Peek(out double priority) {
|
||||
if (Count == 0) {
|
||||
priority = 0.0;
|
||||
return default(T);
|
||||
}
|
||||
priority = A[1].priority;
|
||||
return A[1].v;
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() {
|
||||
for (int i = 1; i <= heapSize; i++)
|
||||
yield return A[i].v;
|
||||
}
|
||||
#if TEST_MSAGL
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string ToString() {
|
||||
StringBuilder sb=new StringBuilder();
|
||||
foreach (var i in this)
|
||||
sb.Append(i + ",");
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7e122f1c5e2b7ce4e91aa5792a19a862
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,270 +0,0 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Msagl.Core.DataStructures {
|
||||
|
||||
// TODO: Move and expand the commented tests below into PriorityQueueTests unit tests for all 3 PQ classes.
|
||||
// TODO: Reduce duplication with GenericBinaryHeapPriorityQueue by breaking most functionality out into a
|
||||
// GenericBinaryHeapPriorityQueue<T, THeapElement> class and using THeapElement.ComparePriority, keeping only Enqueue
|
||||
// and DecreasePriority in the derived classes as these must know about timestamp.
|
||||
|
||||
/// <summary>
|
||||
/// A generic version priority queue based on the binary heap algorithm where
|
||||
/// the priority of each element is passed as a parameter and priority ties are broken by timestamp.
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
|
||||
public class GenericBinaryHeapPriorityQueueWithTimestamp<T> : IEnumerable<T> {
|
||||
|
||||
const int InitialHeapCapacity = 16;
|
||||
|
||||
|
||||
//indexing for A starts from 1
|
||||
|
||||
//[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
//internal void Clear()
|
||||
//{
|
||||
// if(heapSize>0)
|
||||
// {
|
||||
// for(int i=0;i<cache.Length;i++)
|
||||
// this.cache[i]=null;
|
||||
|
||||
// heapSize=0;
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
GenericHeapElementWithTimestamp<T>[] A;//array of heap elements
|
||||
// ReSharper restore InconsistentNaming
|
||||
|
||||
UInt64 timestamp;
|
||||
UInt64 NextTimestamp { get { return ++this.timestamp; } }
|
||||
|
||||
/// <summary>
|
||||
/// it is a mapping from queue elements and their correspondent HeapElements
|
||||
/// </summary>
|
||||
readonly Dictionary<T, GenericHeapElementWithTimestamp<T>> cache;
|
||||
internal int Count { get { return heapSize; } }
|
||||
int heapSize;
|
||||
|
||||
internal GenericBinaryHeapPriorityQueueWithTimestamp() {
|
||||
cache = new Dictionary<T, GenericHeapElementWithTimestamp<T>>();
|
||||
A = new GenericHeapElementWithTimestamp<T>[InitialHeapCapacity + 1];
|
||||
}
|
||||
|
||||
|
||||
void SwapWithParent(int i) {
|
||||
var parent = A[i >> 1];
|
||||
|
||||
PutAtI(i >> 1, A[i]);
|
||||
PutAtI(i, parent);
|
||||
}
|
||||
|
||||
internal void Enqueue(T element, double priority) {
|
||||
if (heapSize == A.Length - 1) {
|
||||
var newA = new GenericHeapElementWithTimestamp<T>[A.Length * 2];
|
||||
Array.Copy(A, 1, newA, 1, heapSize);
|
||||
A = newA;
|
||||
}
|
||||
|
||||
heapSize++;
|
||||
int i = heapSize;
|
||||
GenericHeapElementWithTimestamp<T> h;
|
||||
|
||||
A[i] = cache[element] = h = new GenericHeapElementWithTimestamp<T>(i, priority, element, this.NextTimestamp);
|
||||
while (i > 1 && A[i >> 1].ComparePriority(A[i]) > 0) {
|
||||
SwapWithParent(i);
|
||||
i >>= 1;
|
||||
}
|
||||
System.Diagnostics.Debug.Assert(A[i] == h);
|
||||
A[i] = h;
|
||||
|
||||
}
|
||||
|
||||
void PutAtI(int i, GenericHeapElementWithTimestamp<T> h) {
|
||||
A[i] = h;
|
||||
h.indexToA = i;
|
||||
}
|
||||
|
||||
#if TEST_MSAGL
|
||||
/// <summary>
|
||||
/// Gets the next element to be dequeued, without dequeueing it.
|
||||
/// </summary>
|
||||
internal T Peek() {
|
||||
if (heapSize == 0) {
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
return A[1].v;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the timestamp of the next element to be dequeued, without dequeueing it.
|
||||
/// </summary>
|
||||
internal UInt64 PeekTimestamp() {
|
||||
if (heapSize == 0) {
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
return A[1].Timestamp;
|
||||
}
|
||||
#endif // TEST_MSAGL
|
||||
|
||||
internal T Dequeue() {
|
||||
if (heapSize == 0)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
var ret = A[1].v;
|
||||
|
||||
MoveQueueOneStepForward(ret);
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
void MoveQueueOneStepForward(T ret) {
|
||||
cache.Remove(ret);
|
||||
PutAtI(1, A[heapSize]);
|
||||
int i = 1;
|
||||
while (true) {
|
||||
int smallest = i;
|
||||
int l = i << 1;
|
||||
|
||||
if (l <= heapSize && A[l].ComparePriority(A[i]) < 0)
|
||||
smallest = l;
|
||||
|
||||
int r = l + 1;
|
||||
|
||||
if (r <= heapSize && A[r].ComparePriority(A[smallest]) < 0)
|
||||
smallest = r;
|
||||
|
||||
if (smallest != i)
|
||||
SwapWithParent(smallest);
|
||||
else
|
||||
break;
|
||||
|
||||
i = smallest;
|
||||
|
||||
}
|
||||
|
||||
heapSize--;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// sets the object priority to c
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal void DecreasePriority(T element, double newPriority)
|
||||
{
|
||||
GenericHeapElementWithTimestamp<T> h;
|
||||
//ignore the element if it is not in the queue
|
||||
if (!cache.TryGetValue(element, out h)) return;
|
||||
|
||||
//var h = cache[element];
|
||||
h.priority = newPriority;
|
||||
int i = h.indexToA;
|
||||
while (i > 1) {
|
||||
if (A[i].ComparePriority(A[i >> 1]) < 0)
|
||||
SwapWithParent(i);
|
||||
else
|
||||
break;
|
||||
i >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
///<summary>
|
||||
///</summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
// public static void Test() {
|
||||
// Timer nm = new Timer();
|
||||
// var bound = 1000000;
|
||||
// nm.Start();
|
||||
// {
|
||||
// var q = new BinaryHeapPriorityQueue(bound + 1);
|
||||
// Random random = new Random();
|
||||
// for (int i = 0; i < bound; i++) {
|
||||
// q.Enqueue(i, random.NextDouble());
|
||||
// }
|
||||
// for (int i = 0; i < bound; i++) {
|
||||
// q.DecreasePriority(i, -random.NextDouble());
|
||||
// }
|
||||
//
|
||||
// while (q.IsEmpty() == false) {
|
||||
// q.Dequeue();
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// nm.Stop();
|
||||
// Console.WriteLine(nm.Duration);
|
||||
// nm.Start();
|
||||
// {
|
||||
// var q = new GenericBinaryHeapPriorityQueue<int>();
|
||||
// Random random = new Random();
|
||||
// for (int i = 0; i < bound; i++) {
|
||||
// q.Enqueue(i, random.NextDouble());
|
||||
// }
|
||||
// for (int i = 0; i < bound; i++) {
|
||||
// q.DecreasePriority(i, -random.NextDouble());
|
||||
// }
|
||||
//
|
||||
// while (q.IsEmpty() == false) {
|
||||
// q.Dequeue();
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// nm.Stop();
|
||||
// Console.WriteLine(nm.Duration);
|
||||
//}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
public static void Test() {
|
||||
var q = new GenericBinaryHeapPriorityQueue<int>();
|
||||
q.Enqueue(2, 2);
|
||||
q.Enqueue(1, 1);
|
||||
q.Enqueue(9, 9);
|
||||
q.Enqueue(8, 8);
|
||||
q.Enqueue(5, 5);
|
||||
q.Enqueue(3, 3);
|
||||
q.Enqueue(4, 4);
|
||||
q.Enqueue(7, 7);
|
||||
q.Enqueue(6, 6);
|
||||
q.Enqueue(0, 0);
|
||||
|
||||
q.DecreasePriority(4, 2.5);
|
||||
|
||||
while (q.IsEmpty() == false)
|
||||
Console.WriteLine(q.Dequeue());
|
||||
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// enumerator
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IEnumerator<T> GetEnumerator() {
|
||||
for (int i = 1; i <= heapSize; i++)
|
||||
yield return A[i].v;
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() {
|
||||
for (int i = 1; i <= heapSize; i++)
|
||||
yield return A[i].v;
|
||||
}
|
||||
#if TEST_MSAGL
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string ToString() {
|
||||
var sb=new StringBuilder();
|
||||
foreach (var i in this)
|
||||
sb.Append(i + ",");
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a84290ac5fe1bdb4f9bd404eb9dfc396
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
namespace Microsoft.Msagl.Core.DataStructures {
|
||||
class GenericHeapElement<T> {
|
||||
internal int indexToA;
|
||||
internal double priority;
|
||||
internal T v;//value
|
||||
internal GenericHeapElement(int index, double priority, T v) {
|
||||
indexToA = index;
|
||||
this.priority = priority;
|
||||
this.v = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bc99aab509d5d5846b25d73984a920c1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace Microsoft.Msagl.Core.DataStructures {
|
||||
/// <summary>
|
||||
/// A priority queue element that is compared by priority and timestamp
|
||||
/// </summary>
|
||||
internal class GenericHeapElementWithTimestamp<T> : GenericHeapElement<T> {
|
||||
// Timestamp operates as a tiebreaker when priorities are equal.
|
||||
internal UInt64 Timestamp { get; set; }
|
||||
|
||||
internal GenericHeapElementWithTimestamp(int index, double priority, T v, UInt64 timestamp)
|
||||
: base(index, priority, v) {
|
||||
this.Timestamp = timestamp;
|
||||
}
|
||||
|
||||
internal int ComparePriority(GenericHeapElementWithTimestamp<T> other) {
|
||||
var cmp = base.priority.CompareTo(other.priority);
|
||||
|
||||
// Consider a more recent sequence to have a lower priority value (== higher priority)
|
||||
// so reverse the direction of comparison. Note: Timestamp only applies in the case
|
||||
// of tied priorities, so if it wraps, we'll have suboptimal processing and likely a different
|
||||
// path in the event of ties, but will not fail.
|
||||
return (cmp != 0) ? cmp : other.Timestamp.CompareTo(this.Timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 91406a9d3ae7e79408f53eaac222784e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2db8c0017da4d1e42a8df84b52235c5b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Msagl.Core.DataStructures {
|
||||
internal class ComparerOnDelegate<T> : IComparer<T> {
|
||||
readonly Func<T, T, int> comparer;
|
||||
|
||||
public ComparerOnDelegate(Func<T, T, int> compare) {
|
||||
comparer= compare;
|
||||
}
|
||||
|
||||
public int Compare(T x, T y) {
|
||||
return comparer(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 808dfa443622ca94d9f1a9e8cd9c13be
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Msagl.Core.DataStructures {
|
||||
internal class DefaultComperer<T>: IComparer<T> {
|
||||
public int Compare(T x, T y) {
|
||||
return ((IComparable<T>) x).CompareTo(y);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9bcd9db072a6903479507b3a95b5d657
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
namespace Microsoft.Msagl.Core.DataStructures {
|
||||
|
||||
internal enum RBColor {
|
||||
Red,
|
||||
Black
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ca5c296797071684880042932e6df5ee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
using System;
|
||||
namespace Microsoft.Msagl.Core.DataStructures {
|
||||
|
||||
|
||||
#if TEST_MSAGL
|
||||
[Serializable]
|
||||
#endif
|
||||
internal class RBNode<T> {
|
||||
|
||||
internal RBColor color;
|
||||
internal T Item;
|
||||
internal RBNode<T> parent, left, right;
|
||||
internal RBNode(RBColor color) { this.color = color; }
|
||||
internal RBNode(RBColor color, T item, RBNode<T> p, RBNode<T> left, RBNode<T> right) {
|
||||
this.color = color;
|
||||
this.parent = p;
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
this.Item = item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string ToString() {
|
||||
return Item.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: eb455e0993b23bc45814a25cd54d4a50
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,483 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Msagl.Core.DataStructures {
|
||||
|
||||
//public class TestRB :IComparer<int>{
|
||||
// public static void Test() {
|
||||
// var tree = new RBTree<int>(new TestRB());
|
||||
// tree.insert(1);
|
||||
// tree.insert(6);
|
||||
// tree.insert(8);
|
||||
// tree.insert(11);
|
||||
// tree.insert(13);
|
||||
// tree.insert(17);
|
||||
// tree.insert(15);
|
||||
// tree.insert(25);
|
||||
// tree.insert(22);
|
||||
// tree.insert(27);
|
||||
// tree.insert(16);
|
||||
|
||||
// var t = tree.findLast(delegate(int i) { return i <= 15.5; });
|
||||
|
||||
// }
|
||||
|
||||
|
||||
// #region IComparer<int> Members
|
||||
|
||||
// public int Compare(int x, int y) {
|
||||
// return x - y;
|
||||
// }
|
||||
|
||||
// #endregion
|
||||
//}
|
||||
|
||||
#if TEST_MSAGL
|
||||
[Serializable]
|
||||
#endif
|
||||
internal class RbTree<T> : IEnumerable<T> {
|
||||
|
||||
/// <summary>
|
||||
/// find the first, minimal, node in the tree such that predicate holds
|
||||
/// </summary>
|
||||
/// <param name="predicate">Has to be monotone in the sense that if it holds for t then it holds for any t' greater or equal than t
|
||||
/// so the predicate values have a form (false, false, ..., false, true, true, ..., true)
|
||||
/// </param>
|
||||
/// <returns>the first node where predicate holds or null</returns>
|
||||
internal RBNode<T> FindFirst(Func<T, bool> predicate) {
|
||||
return FindFirst(root, predicate);
|
||||
}
|
||||
|
||||
internal RbTree() {
|
||||
comparer = new DefaultComperer<T>();
|
||||
root = nil = new RBNode<T>(RBColor.Black);
|
||||
}
|
||||
|
||||
RBNode<T> FindFirst(RBNode<T> n, Func<T, bool> p) {
|
||||
if (n == nil)
|
||||
return null;
|
||||
RBNode<T> good = null;
|
||||
while (n != nil)
|
||||
n = p(n.Item) ? (good = n).left : n.right;
|
||||
|
||||
return good;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// find the last, maximal, node in the tree such that predicate holds
|
||||
/// </summary>
|
||||
/// <param name="predicate">Has to be monotone in the sense that if it holds for t then it holds for any t' less or equal than t
|
||||
/// so the predicate values on the tree have a form (true, true, ..., true, false, false, ..., false)
|
||||
/// </param>
|
||||
/// <returns>the last node where predicate holds or null</returns>
|
||||
internal RBNode<T> FindLast(Func<T,bool> predicate) {
|
||||
return FindLast(root, predicate);
|
||||
}
|
||||
|
||||
RBNode<T> FindLast(RBNode<T> n, Func<T,bool> p) {
|
||||
if (n == nil)
|
||||
return null;
|
||||
RBNode<T> good = null;
|
||||
while (n != nil)
|
||||
n = p(n.Item) ? (good = n).right : n.left;
|
||||
|
||||
return good;
|
||||
}
|
||||
|
||||
|
||||
readonly IComparer<T> comparer;
|
||||
|
||||
IComparer<T> Comparer {
|
||||
get { return comparer; }
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator() { return new RBTreeEnumerator<T>(this); }
|
||||
|
||||
RBNode<T> nil;
|
||||
|
||||
internal RBNode<T> Nil { get { return nil; } }
|
||||
|
||||
RBNode<T> root;
|
||||
internal RBNode<T> Root { get { return root; } }
|
||||
|
||||
internal RBNode<T> Next(RBNode<T> x) {
|
||||
if (x.right != nil)
|
||||
return TreeMinimum(x.right);
|
||||
RBNode<T> y = x.parent;
|
||||
while (y != nil && x == y.right) {
|
||||
x = y;
|
||||
y = y.parent;
|
||||
}
|
||||
return ToNull(y);
|
||||
}
|
||||
|
||||
RBNode<T> ToNull(RBNode<T> y) {
|
||||
return y != nil ? y : null;
|
||||
}
|
||||
|
||||
internal RBNode<T> Previous(RBNode<T> x) {
|
||||
if (x.left != nil)
|
||||
return TreeMaximum(x.left);
|
||||
RBNode<T> y = x.parent;
|
||||
while (y != nil && x == y.left) {
|
||||
x = y;
|
||||
y = y.parent;
|
||||
}
|
||||
return ToNull(y);
|
||||
}
|
||||
|
||||
RBNode<T> TreeMinimum(RBNode<T> x) {
|
||||
while (x.left != nil)
|
||||
x = x.left;
|
||||
return ToNull(x);
|
||||
}
|
||||
|
||||
internal RBNode<T> TreeMinimum() {
|
||||
return TreeMinimum(root);
|
||||
}
|
||||
|
||||
|
||||
RBNode<T> TreeMaximum(RBNode<T> x) {
|
||||
while (x.right != nil)
|
||||
x = x.right;
|
||||
return ToNull(x);
|
||||
}
|
||||
|
||||
internal RBNode<T> TreeMaximum() {
|
||||
return TreeMaximum(root);
|
||||
}
|
||||
|
||||
|
||||
//RBTree<T> Clone() {
|
||||
// RBTree<T> clone = new RBTree<T>(this.Comparer);
|
||||
// foreach (T n in this) {
|
||||
// clone.insert(n);
|
||||
// }
|
||||
// return clone;
|
||||
//}
|
||||
|
||||
|
||||
public override string ToString() {
|
||||
string ret = "{";
|
||||
int i = 0;
|
||||
foreach (T p in this) {
|
||||
ret += p.ToString();
|
||||
if (i != count - 1) {
|
||||
ret += ",";
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return ret + "}";
|
||||
}
|
||||
|
||||
|
||||
internal RBNode<T> DeleteSubtree(RBNode<T> z) {
|
||||
System.Diagnostics.Debug.Assert(z != nil);
|
||||
|
||||
RBNode<T> y;
|
||||
if (z.left == nil || z.right == nil) {
|
||||
/* y has a nil node as a child */
|
||||
y = z;
|
||||
} else {
|
||||
/* find tree successor with a nil node as a child */
|
||||
y = z.right;
|
||||
while (y.left != nil) y = y.left;
|
||||
}
|
||||
|
||||
/* x is y's only child */
|
||||
RBNode<T> x = y.left != nil ? y.left : y.right;
|
||||
|
||||
x.parent = y.parent;
|
||||
if (y.parent == nil)
|
||||
root = x;
|
||||
else {
|
||||
if (y == y.parent.left)
|
||||
y.parent.left = x;
|
||||
else
|
||||
y.parent.right = x;
|
||||
}
|
||||
if (y != z)
|
||||
z.Item = y.Item;
|
||||
if (y.color == RBColor.Black)
|
||||
DeleteFixup(x);
|
||||
|
||||
// checkTheTree();
|
||||
|
||||
return ToNull(z);
|
||||
|
||||
}
|
||||
|
||||
int count;
|
||||
public int Count { get { return count; } }
|
||||
|
||||
internal RBNode<T> Remove(T i) {
|
||||
RBNode<T> n = Find(i);
|
||||
if (n != null) {
|
||||
count--;
|
||||
return DeleteSubtree(n);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
internal void DeleteNodeInternal(RBNode<T> x) {
|
||||
count--;
|
||||
DeleteSubtree(x);
|
||||
}
|
||||
|
||||
RBNode<T> Find(RBNode<T> x, T i) {
|
||||
int compareResult;
|
||||
while (x != nil && (compareResult = Comparer.Compare(i, x.Item)) != 0)
|
||||
x = compareResult < 0 ? x.left : x.right;
|
||||
|
||||
return ToNull(x);
|
||||
}
|
||||
|
||||
internal RBNode<T> Find(T i) {
|
||||
return Find(root, i);
|
||||
}
|
||||
|
||||
void DeleteFixup(RBNode<T> x) {
|
||||
while (x != root && x.color == RBColor.Black) {
|
||||
if (x == x.parent.left) {
|
||||
RBNode<T> w = x.parent.right;
|
||||
if (w.color == RBColor.Red) {
|
||||
w.color = RBColor.Black;
|
||||
x.parent.color = RBColor.Red;
|
||||
LeftRotate(x.parent);
|
||||
w = x.parent.right;
|
||||
}
|
||||
if (w.left.color == RBColor.Black && w.right.color == RBColor.Black) {
|
||||
w.color = RBColor.Red;
|
||||
x = x.parent;
|
||||
} else {
|
||||
if (w.right.color == RBColor.Black) {
|
||||
w.left.color = RBColor.Black;
|
||||
w.color = RBColor.Red;
|
||||
RightRotate(w);
|
||||
w = x.parent.right;
|
||||
}
|
||||
w.color = x.parent.color;
|
||||
x.parent.color = RBColor.Black;
|
||||
w.right.color = RBColor.Black;
|
||||
LeftRotate(x.parent);
|
||||
x = root;
|
||||
}
|
||||
} else {
|
||||
RBNode<T> w = x.parent.left;
|
||||
if (w.color == RBColor.Red) {
|
||||
w.color = RBColor.Black;
|
||||
x.parent.color = RBColor.Red;
|
||||
RightRotate(x.parent);
|
||||
w = x.parent.left;
|
||||
}
|
||||
if (w.right.color == RBColor.Black && w.left.color == RBColor.Black) {
|
||||
w.color = RBColor.Red;
|
||||
x = x.parent;
|
||||
} else {
|
||||
if (w.left.color == RBColor.Black) {
|
||||
w.right.color = RBColor.Black;
|
||||
w.color = RBColor.Red;
|
||||
LeftRotate(w);
|
||||
w = x.parent.left;
|
||||
}
|
||||
w.color = x.parent.color;
|
||||
x.parent.color = RBColor.Black;
|
||||
w.left.color = RBColor.Black;
|
||||
RightRotate(x.parent);
|
||||
x = root;
|
||||
}
|
||||
}
|
||||
}
|
||||
x.color = RBColor.Black;
|
||||
}
|
||||
|
||||
internal bool IsEmpty() { return root == nil; }
|
||||
|
||||
RBNode<T> TreeInsert(T z) {
|
||||
var y = nil;
|
||||
var x = root;
|
||||
var compareRes = 0;
|
||||
while (x != nil) {
|
||||
y = x;
|
||||
#if SHARPKIT //https://code.google.com/p/sharpkit/issues/detail?id=368
|
||||
compareRes = Comparer.Compare(z, x.Item);
|
||||
x = compareRes < 0 ? x.left : x.right;
|
||||
#else
|
||||
x = (compareRes = Comparer.Compare(z, x.Item)) < 0 ? x.left : x.right;
|
||||
#endif
|
||||
}
|
||||
|
||||
var nz = new RBNode<T>(RBColor.Black, z, y, nil, nil);
|
||||
|
||||
if (y == nil)
|
||||
root = nz;
|
||||
else if (compareRes < 0)
|
||||
y.left = nz;
|
||||
else
|
||||
y.right = nz;
|
||||
|
||||
return ToNull(nz);
|
||||
}
|
||||
|
||||
void InsertPrivate(RBNode<T> x) {
|
||||
count++;
|
||||
x.color = RBColor.Red;
|
||||
while (x != root && x.parent.color == RBColor.Red) {
|
||||
if (x.parent == x.parent.parent.left) {
|
||||
RBNode<T> y = x.parent.parent.right;
|
||||
if (y.color == RBColor.Red) {
|
||||
x.parent.color = RBColor.Black;
|
||||
y.color = RBColor.Black;
|
||||
x.parent.parent.color = RBColor.Red;
|
||||
x = x.parent.parent;
|
||||
} else {
|
||||
if (x == x.parent.right) {
|
||||
x = x.parent;
|
||||
LeftRotate(x);
|
||||
}
|
||||
x.parent.color = RBColor.Black;
|
||||
x.parent.parent.color = RBColor.Red;
|
||||
RightRotate(x.parent.parent);
|
||||
}
|
||||
} else {
|
||||
RBNode<T> y = x.parent.parent.left;
|
||||
if (y.color == RBColor.Red) {
|
||||
x.parent.color = RBColor.Black;
|
||||
y.color = RBColor.Black;
|
||||
x.parent.parent.color = RBColor.Red;
|
||||
x = x.parent.parent;
|
||||
} else {
|
||||
if (x == x.parent.left) {
|
||||
x = x.parent;
|
||||
RightRotate(x);
|
||||
}
|
||||
x.parent.color = RBColor.Black;
|
||||
x.parent.parent.color = RBColor.Red;
|
||||
LeftRotate(x.parent.parent);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
root.color = RBColor.Black;
|
||||
|
||||
//checkTheTree();
|
||||
}
|
||||
|
||||
internal RBNode<T> Insert(T v) {
|
||||
RBNode<T> x = TreeInsert(v);
|
||||
InsertPrivate(x);
|
||||
return ToNull(x);
|
||||
}
|
||||
|
||||
void LeftRotate(RBNode<T> x) {
|
||||
RBNode<T> y = x.right;
|
||||
x.right = y.left;
|
||||
if (y.left != nil)
|
||||
y.left.parent = x;
|
||||
y.parent = x.parent;
|
||||
if (x.parent == nil)
|
||||
root = y;
|
||||
else if (x == x.parent.left)
|
||||
x.parent.left = y;
|
||||
else
|
||||
x.parent.right = y;
|
||||
|
||||
y.left = x;
|
||||
x.parent = y;
|
||||
}
|
||||
|
||||
void RightRotate(RBNode<T> x) {
|
||||
RBNode<T> y = x.left;
|
||||
x.left = y.right;
|
||||
if (y.right != nil)
|
||||
y.right.parent = x;
|
||||
y.parent = x.parent;
|
||||
if (x.parent == nil)
|
||||
root = y;
|
||||
else if (x == x.parent.right)
|
||||
x.parent.right = y;
|
||||
else
|
||||
x.parent.left = y;
|
||||
|
||||
y.right = x;
|
||||
x.parent = y;
|
||||
|
||||
}
|
||||
|
||||
internal RbTree(Func<T, T, int> func) : this(new ComparerOnDelegate<T>(func)) {}
|
||||
|
||||
|
||||
internal RbTree(IComparer<T> comparer) {
|
||||
Clear();
|
||||
this.comparer = comparer;
|
||||
}
|
||||
|
||||
internal void Clear() {
|
||||
root = nil = new RBNode<T>(RBColor.Black);
|
||||
}
|
||||
|
||||
//void checkTheTree() {
|
||||
// int blacks = -1;
|
||||
// checkBlacks(root, 0, ref blacks);
|
||||
// checkColors(root);
|
||||
|
||||
//}
|
||||
|
||||
|
||||
|
||||
//void checkBlacks(RBNode<T> node, int nnow, ref int blacks) {
|
||||
// if (node != nil) {
|
||||
// RBNode<T> l = node.left, r = node.right;
|
||||
|
||||
// if (l != nil)
|
||||
// if (Comparer.Compare(l.item,node.item) >= 0)
|
||||
// throw new Exception("order");
|
||||
|
||||
// if (r != nil)
|
||||
// if (Comparer.Compare(r.item,node.item) <= 0)
|
||||
// throw new Exception("order");
|
||||
|
||||
|
||||
// }
|
||||
|
||||
// if (node == nil) {
|
||||
// if (blacks != nnow + 1 && blacks != -1)
|
||||
// throw new Exception("blacks");
|
||||
|
||||
// blacks = nnow + 1;
|
||||
|
||||
// } else if (node.color == RBColor.Black) {
|
||||
// checkBlacks(node.left, nnow + 1, ref blacks);
|
||||
// checkBlacks(node.right, nnow + 1, ref blacks);
|
||||
// } else {
|
||||
// checkBlacks(node.left, nnow, ref blacks);
|
||||
// checkBlacks(node.right, nnow, ref blacks);
|
||||
// }
|
||||
|
||||
|
||||
//}
|
||||
|
||||
//void checkColors(RBNode<T> x) {
|
||||
// if (x == nil)
|
||||
// return;
|
||||
// //property 3
|
||||
// if (x.color == RBColor.Red)
|
||||
// if (x.left.color == RBColor.Red || x.right.color == RBColor.Red)
|
||||
// throw new Exception("colors");
|
||||
|
||||
// checkColors(x.left);
|
||||
// checkColors(x.right);
|
||||
//}
|
||||
|
||||
#region IEnumerable Members
|
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
|
||||
return new RBTreeEnumerator<T>(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a151171447e096b488f0330e292d99bb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue