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:
Reinier Vens 2021-01-31 03:52:32 +00:00
commit dbc522a224
2105 changed files with 7033 additions and 1141 deletions

View file

@ -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;
}
}

View file

@ -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!");
}

View file

@ -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();
}

View file

@ -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);
}

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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();
}
}

View file

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: ec3325b6b8b1a504b8de9e3553bce449
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 48b22515742194f478ce494a467011b0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}

View file

@ -1,5 +0,0 @@
fileFormatVersion: 2
guid: e1b0b5fd6a4e44923b40d856017562e1
ShaderImporter:
defaultTextures: []
userData:

View file

@ -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;
}

View file

@ -1,5 +0,0 @@
fileFormatVersion: 2
guid: 3ba1abd1cb44c47b68fbb577c6c4ba3e
ShaderImporter:
defaultTextures: []
userData:

View file

@ -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);
}

View file

@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: 0a38e0bb6f84203469f3e9cb8545ffce
timeCreated: 1448775185
licenseType: Pro
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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);
}

View file

@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: b577a94f58a1398409bfcfe9cd5dbf99
timeCreated: 1448788105
licenseType: Pro
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}

View file

@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: 33c8157a2aca37849bc1f21250315a93
timeCreated: 1449292284
licenseType: Pro
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}

View file

@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: a7111eafc2463224faacf7be3bddb9a4
timeCreated: 1449292284
licenseType: Pro
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

View file

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 4a109a0b0bb6e8f40935ca2ec55e5bd7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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());
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: adf902e96e0c51049b65bc002d6ef4ca
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: ee065f025d2f5374db2ceaa973b822d4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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&lt;TVertex, TFace&gt;.
/// </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&lt;TVertex, DefaultConvexFace&lt;TVertex&gt;&gt;.
/// </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&lt;DefaultVertex, DefaultConvexFace&lt;DefaultVertex&gt;&gt;.
/// </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&lt;TVertex, TFace&gt;.
/// </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);
}
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 447289e858e19c14abbe91dfbd0e3089
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: a6e88469e3b81a541a0bc8a66e60d0c7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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
/// =&gt; 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;
}
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 3969283304824fe4a8eca1eb522fd3a7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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
{
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 5db1a96860ec6d949930695ae9bedcc0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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&lt;TVertex, TFace&gt;.
/// </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&lt;System.Int32&gt;.</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
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 48bd00cb6ec8d244f90e1c1608f39074
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: f7f265217d00ea340b87645242a45181
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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];
}
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 58c58e163f4c86e48a6df3ee6c5056c0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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 &gt; 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];
}
}
}
}
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 8bb3327041efa18479319ec117493f8c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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();
}
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: e8d582e97c6824740ba9e1a41641b372
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -1,9 +0,0 @@
using UnityEngine;
using System.Collections;
using MIConvexHull;
public class Face3 : ConvexFace<Vertex3, Face3>
{
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 30a2635ffe51545499499ce392b234c8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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; }
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: a55f713690988bb48aef7cbd4f01ebc7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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&lt;TVertex, DefaultTriangulationCell&lt;TVertex&gt;&gt;.</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&lt;DefaultVertex, DefaultTriangulationCell&lt;DefaultVertex&gt;&gt;.</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&lt;TVertex, TFace&gt;.</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&lt;TVertex, TCell, TEdge&gt;.</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&lt;TVertex, DefaultTriangulationCell&lt;TVertex&gt;, VoronoiEdge&lt;TVertex, DefaultTriangulationCell&lt;TVertex&gt;&gt;&gt;.</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&lt;DefaultVertex, DefaultTriangulationCell&lt;DefaultVertex&gt;, VoronoiEdge&lt;DefaultVertex, DefaultTriangulationCell&lt;DefaultVertex&gt;&gt;&gt;.</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&lt;TVertex, TCell, VoronoiEdge&lt;TVertex, TCell&gt;&gt;.</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);
}
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 0762bfe861ed97749a222fb1c217788a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 5bf99a94223c6e1458c243cb68091852
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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();
}
}
}
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: de8661d86f2667647b9dca59c1c97e9a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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&lt;TVertex, TCell&gt;.</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 };
}
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 4b0e872e78d2ab8428b446cfa5c06580
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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
{
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 808182249e0e66143b65945d9aeb37fe
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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();
}
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 2f5c388c78c121a47b14c2f4ed26af38
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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&lt;TVertex, TCell, TEdge&gt;.</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&lt;TVertex, DefaultTriangulationCell&lt;TVertex&gt;, VoronoiEdge&lt;TVertex, DefaultTriangulationCell&lt;TVertex&gt;&gt;&gt;.</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&lt;DefaultVertex, DefaultTriangulationCell&lt;DefaultVertex&gt;, VoronoiEdge&lt;DefaultVertex, DefaultTriangulationCell&lt;DefaultVertex&gt;&gt;&gt;.</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&lt;TVertex, TCell, VoronoiEdge&lt;TVertex, TCell&gt;&gt;.</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&lt;TVertex, TCell, TEdge&gt;.</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();
}
}
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: fbb5750f20479844eb4e504302755db9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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));
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 67d3ec22ea52a2642a14ff864e3017b0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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]);
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 3553f231414f28347996a803a79425fa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: a80bb5b7ce4059d40a0eddd80b4b4ae5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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>

View file

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 40dcc7efbe87de246a2a5146c5128222
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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>

View file

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 64a9bda5ccebb9e45b4c45a2f2d7d9da
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: f3f9e18906f0e744eb7e5b695467d1cc
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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 &lt; progressRatio &lt;= 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 &lt; progressRatio &lt;= 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
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 8f0e154226de7c44d93b02694d82946a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 838ad0009928e604e94e5580a15ada26
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 500f7d7386669de4abbd734ecd9ae3c3
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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());
}
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: a62428fef9112304eb0439efc749dc37
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 78dc14a0e35a20145a57e0314da9ab9b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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);
}
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 6e8b582a3d0c32a41bc4eb7f0db98f96
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 7e122f1c5e2b7ce4e91aa5792a19a862
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: a84290ac5fe1bdb4f9bd404eb9dfc396
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: bc99aab509d5d5846b25d73984a920c1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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);
}
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 91406a9d3ae7e79408f53eaac222784e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 2db8c0017da4d1e42a8df84b52235c5b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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);
}
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 808dfa443622ca94d9f1a9e8cd9c13be
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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);
}
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 9bcd9db072a6903479507b3a95b5d657
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -1,8 +0,0 @@
namespace Microsoft.Msagl.Core.DataStructures {
internal enum RBColor {
Red,
Black
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: ca5c296797071684880042932e6df5ee
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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();
}
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: eb455e0993b23bc45814a25cd54d4a50
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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
}
}

View file

@ -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