add ball scaling example

This commit is contained in:
Chikashi Miyama 2019-10-02 18:17:08 +02:00
parent e23c615465
commit 837d1bdf85
19 changed files with 811 additions and 600 deletions

View file

@ -13,9 +13,10 @@
<e p="PdBackendEditor.cs" t="Include" />
<e p="PdSpectrumBindEditor.cs" t="Include" />
<e p="RectangularSelection.cs" t="Include" />
<e p="SpectrumGenerator.cs" t="Include" />
<e p="SpectrumGeneratorEditMode.cs" t="Include" />
<e p="UnitTest" t="Include">
<e p="UnitTest_ChangeObserver.cs" t="Include" />
<e p="UnitTest_RectangularSelection.cs" t="Include" />
<e p="UnitTest_SpectrumGenerator.cs" t="Include" />
</e>
</e>
@ -30,17 +31,24 @@
<e p="RMSAnalyzer.cs" t="Include" />
</e>
</e>
<e p="PdBackendDemo" t="Include">
<e p="script" t="Include">
<e p="BoomBall.cs" t="Include" />
</e>
</e>
</e>
</e>
<e p="Scripts" t="Include">
<e p="PdConnection" t="Include">
<e p="FFTArrayContainer.cs" t="Include" />
<e p="ISpectrumGenerator.cs" t="Include" />
<e p="PdArray.cs" t="Include" />
<e p="PdBackend.cs" t="Include" />
<e p="PdConstant.cs" t="Include" />
<e p="PdProcess.cs" t="Include" />
<e p="PdSocket.cs" t="Include" />
<e p="PdSpectrumBind.cs" t="Include" />
<e p="SpectrumGeneratorPlayMode.cs" t="Include" />
</e>
<e p="TemplateLibrary" t="Include">
<e p="ChangeObserver.cs" t="Include" />

File diff suppressed because it is too large Load diff

View file

@ -62,7 +62,7 @@
<Compile Include="Assets\Editor\PdBackendEditor.cs" />
<Compile Include="Assets\Editor\PdSpectrumBindEditor.cs" />
<Compile Include="Assets\Editor\RectangularSelection.cs" />
<Compile Include="Assets\Editor\SpectrumGenerator.cs" />
<Compile Include="Assets\Editor\SpectrumGeneratorEditMode.cs" />
<Compile Include="Assets\Editor\UnitTest\UnitTest_ChangeObserver.cs" />
<Compile Include="Assets\Editor\UnitTest\UnitTest_RectangularSelection.cs" />
<Compile Include="Assets\Editor\UnitTest\UnitTest_SpectrumGenerator.cs" />

View file

@ -59,13 +59,16 @@
<ItemGroup>
<Compile Include="Assets\Scenes\Examples\Example1\script\BallMapping.cs" />
<Compile Include="Assets\Scenes\Examples\Example1\script\RmsAnalyzer.cs" />
<Compile Include="Assets\Scenes\Examples\PdBackendDemo\script\BoomBall.cs" />
<Compile Include="Assets\Scripts\PdConnection\FFTArrayContainer.cs" />
<Compile Include="Assets\Scripts\PdConnection\ISpectrumGenerator.cs" />
<Compile Include="Assets\Scripts\PdConnection\PdArray.cs" />
<Compile Include="Assets\Scripts\PdConnection\PdBackend.cs" />
<Compile Include="Assets\Scripts\PdConnection\PdConstant.cs" />
<Compile Include="Assets\Scripts\PdConnection\PdProcess.cs" />
<Compile Include="Assets\Scripts\PdConnection\PdSocket.cs" />
<Compile Include="Assets\Scripts\PdConnection\PdSpectrumBind.cs" />
<Compile Include="Assets\Scripts\PdConnection\SpectrumGeneratorPlayMode.cs" />
<Compile Include="Assets\Scripts\TemplateLibrary\ChangeObserver.cs" />
<Compile Include="Assets\Scripts\Versioning\VersionToggleBehaviour.cs" />
<Compile Include="Assets\Scripts\VideoInput\ComponentFactory.cs" />

View file

@ -6,26 +6,28 @@ namespace cylvester
[CustomEditor(typeof(PdSpectrumBind))]
class PdSpectrumBindEditor : Editor
{
private const int TextureWidth = 512;
private const int TextureHeight = 256;
private readonly string[] channels = {
private readonly string[] channels_ =
{
"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16"
};
private ISpectrumGenerator spectrumGenerator_;
private IRectangularSelection rectangularSelection_;
private Rect paintSpace_;
private SerializedProperty selectionProperty_;
private SerializedProperty pdBackendProperty_;
private SerializedProperty energyChangedProperty_;
private Rect paintSpace_;
private ISpectrumGenerator spectrumGeneratorEditMode_;
public void OnEnable()
{
spectrumGenerator_ = new SpectrumGenerator(TextureWidth, TextureHeight);
rectangularSelection_ = new RectangularSelection(TextureWidth, TextureHeight);
var behaviour = (IPdSpectrumBind) target;
pdBackendProperty_ = serializedObject.FindProperty("pdBackend");
selectionProperty_ = serializedObject.FindProperty("selection");
energyChangedProperty_ = serializedObject.FindProperty("energyChanged");
rectangularSelection_ = new RectangularSelection(behaviour.TextureWidth, behaviour.TextureHeight);
spectrumGeneratorEditMode_ = new SpectrumGeneratorEditMode(behaviour.TextureWidth, behaviour.TextureHeight);
}
public override void OnInspectorGUI()
@ -34,37 +36,42 @@ namespace cylvester
EditorGUILayout.PropertyField(pdBackendProperty_);
GUILayout.Label("PureData Inputs", EditorStyles.boldLabel);
behaviour.Channel = EditorGUILayout.Popup("Input Channel", behaviour.Channel, channels);
behaviour.Channel = EditorGUILayout.Popup("Input Channel", behaviour.Channel, channels_);
RenderSpectrumExtractor(behaviour);
serializedObject.ApplyModifiedProperties();
}
GUILayout.Label("Callback", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(energyChangedProperty_);
private void RenderSpectrumExtractor(IPdSpectrumBind behaviour)
{
RenderSelection();
GUILayout.Space(5);
GUILayout.Label("Spectrum Extractor", EditorStyles.boldLabel);
paintSpace_ = GUILayoutUtility.GetRect(behaviour.TextureWidth, behaviour.TextureWidth,
behaviour.TextureHeight, behaviour.TextureHeight);
UpdateSelection();
var paintSpace = GUILayoutUtility.GetRect(TextureHeight, TextureWidth, TextureHeight, TextureHeight);
if (Event.current.type == EventType.Repaint)
{
paintSpace_ = paintSpace;
IPdArray spectrumArray = null;
// update selection
if (Application.isPlaying)
spectrumArray = behaviour.GetPdArray(behaviour.Channel);
behaviour.Energy = spectrumGenerator_.Update(spectrumArray, selectionProperty_.rectValue);
GUI.DrawTexture(paintSpace_, spectrumGenerator_.Spectrum);
{
GUI.DrawTexture(paintSpace_, behaviour.Spectrum);
}
else
{
spectrumGeneratorEditMode_.Update(selectionProperty_.rectValue);
GUI.DrawTexture(paintSpace_, spectrumGeneratorEditMode_.Spectrum);
}
}
Repaint();
RenderExtractedEnergy(behaviour.Energy);
serializedObject.ApplyModifiedProperties();
}
private void RenderSelection()
private void UpdateSelection()
{
if (!Event.current.isMouse || Event.current.button != 0) return;
switch (Event.current.type)
@ -74,9 +81,11 @@ namespace cylvester
rectangularSelection_.Start(Event.current.mousePosition);
break;
}
case EventType.MouseDrag:
{
selectionProperty_.rectValue = rectangularSelection_.Update(Event.current.mousePosition, ref paintSpace_);
selectionProperty_.rectValue =
rectangularSelection_.Update(Event.current.mousePosition, ref paintSpace_);
break;
}
}
@ -89,6 +98,5 @@ namespace cylvester
GUILayout.Label(energy.ToString());
GUILayout.EndHorizontal();
}
}
}

View file

@ -1,85 +0,0 @@
using System;
using UnityEngine;
namespace cylvester
{
interface ISpectrumGenerator
{
Texture2D Spectrum { get; }
int Update(IPdArray pdArray, Rect selectionRect);
}
public class SpectrumGenerator : ISpectrumGenerator
{
public Texture2D Spectrum { get; }
public SpectrumGenerator(int width, int height)
{
Spectrum = new Texture2D(width, height);
}
public int Update(IPdArray pdArray, Rect selectionRect)
{
if (pdArray != null)
{
return UpdatePlayMode(pdArray, selectionRect);
}
UpdateEditMode(selectionRect);
return 0;
}
private int UpdatePlayMode(IPdArray pdArray, Rect selectionRect)
{
var numPixels = 0;
var data = pdArray.Data;
OnAllPixels((x, y) =>
{
var magnitude = data[x] * 20f;
var validPixel = magnitude > y;
var color = validPixel ? Color.green : Color.black;
if (IsInSelection(x, y, ref selectionRect))
{
color.a = 1f;
if (validPixel)
numPixels++;
}
else
color.a = 0.2f;
Spectrum.SetPixel(x, y, color);
});
Spectrum.Apply();
return numPixels;
}
private void UpdateEditMode(Rect selectionRect)
{
OnAllPixels((x, y) =>
{
var color = Color.black;
if (IsInSelection(x, y, ref selectionRect))
color.a = 1f;
else
color.a = 0.2f;
Spectrum.SetPixel(x, y, color);
});
Spectrum.Apply();
}
private void OnAllPixels(Action<int, int> action)
{
for (var x = 0; x < Spectrum.width; x++)
for (var y = 0; y < Spectrum.height; y++)
action(x, y);
}
private bool IsInSelection(int x, int y, ref Rect selectionRect)
{
var inRectHorizontally = selectionRect.x < x && x < selectionRect.x + (selectionRect.width-1);
var mY = Spectrum.height - selectionRect.y;
var inRectVertically = mY - (selectionRect.height-1) < y && y < mY;
return inRectHorizontally && inRectVertically;
}
}
}

View file

@ -0,0 +1,26 @@
using UnityEngine;
namespace cylvester
{
public class SpectrumGeneratorEditMode : SpectrumGenerator, ISpectrumGenerator
{
public SpectrumGeneratorEditMode(int textureWidth, int textureHeight)
: base(textureWidth,textureHeight) { }
public int Update(Rect selectionRect)
{
OnAllPixels((x, y) =>
{
var color = Color.black;
if (IsInSelection(x, y, ref selectionRect))
color.a = 1f;
else
color.a = 0.2f;
Spectrum.SetPixel(x, y, color);
});
Spectrum.Apply();
return 0;
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 50379fecf12e4116b2de1cab55fda6b3
timeCreated: 1570029604

View file

@ -26,7 +26,7 @@ namespace cylvester
standardColor_ = new Color(0f, 0f, 0f, 0.2f);
selectedColor_ = new Color(0f, 0f, 0f, 1f);
}
/*
[Test]
public void Construction()
{
@ -107,5 +107,7 @@ namespace cylvester
}
}
}
*/
}
}

View file

@ -121,76 +121,15 @@ GameObject:
serializedVersion: 6
m_Component:
- component: {fileID: 127705019}
- component: {fileID: 127705018}
- component: {fileID: 127705017}
- component: {fileID: 127705020}
m_Layer: 0
m_Name: Cube
m_Name: SpectrumBind
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!23 &127705017
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 127705016}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 0
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
--- !u!33 &127705018
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 127705016}
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
--- !u!4 &127705019
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 127705016}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 4.8635645, y: -4.5257893, z: 12.978625}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 3
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &127705020
--- !u!114 &127705017
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
@ -205,10 +144,40 @@ MonoBehaviour:
pdBackend: {fileID: 987772533}
selection:
serializedVersion: 2
x: 297.75885
y: 55
width: 101.67376
height: 102
x: 34.430885
y: 99
width: 56.747196
height: 97
energyChanged:
m_PersistentCalls:
m_Calls:
- m_Target: {fileID: 428795649}
m_MethodName: set_Size
m_Mode: 0
m_Arguments:
m_ObjectArgument: {fileID: 0}
m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
m_IntArgument: 0
m_FloatArgument: 0
m_StringArgument:
m_BoolArgument: 0
m_CallState: 2
m_TypeName: cylvester.UnityFloatEvent, Assembly-CSharp, Version=0.0.0.0, Culture=neutral,
PublicKeyToken=null
--- !u!4 &127705019
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 127705016}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 3
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &267275365
GameObject:
m_ObjectHideFlags: 0
@ -285,12 +254,102 @@ Transform:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 267275365}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 1, z: -10}
m_LocalPosition: {x: 0, y: 0, z: -5}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &428795648
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 428795652}
- component: {fileID: 428795651}
- component: {fileID: 428795650}
- component: {fileID: 428795649}
m_Layer: 0
m_Name: BoomBall
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &428795649
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 428795648}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 87673687b9b5a10488287f6faecad9d0, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!23 &428795650
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 428795648}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 0
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
--- !u!33 &428795651
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 428795648}
m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
--- !u!4 &428795652
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 428795648}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 4
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &987772532
GameObject:
m_ObjectHideFlags: 0

View file

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

View file

@ -0,0 +1,23 @@
using UnityEngine;
namespace cylvester
{
interface IBoomBall
{
float Size { set; }
}
public class BoomBall : MonoBehaviour, IBoomBall
{
public float Size
{
set
{
var scale = value * 0.01f + 0.1f;
transform.localScale = new Vector3(scale, scale, scale);
}
}
}
}

View file

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

View file

@ -0,0 +1,36 @@
using System;
using UnityEngine;
namespace cylvester
{
public interface ISpectrumGenerator
{
Texture2D Spectrum { get; }
int Update(Rect selectionRect);
}
public abstract class SpectrumGenerator
{
public Texture2D Spectrum { get; }
protected SpectrumGenerator(int textureWidth, int textureHeight)
{
Spectrum = new Texture2D(textureWidth, textureHeight);
}
protected void OnAllPixels(Action<int, int> action)
{
for (var x = 0; x < Spectrum.width; x++)
for (var y = 0; y < Spectrum.height; y++)
action(x, y);
}
protected bool IsInSelection(int x, int y, ref Rect selectionRect)
{
var inRectHorizontally = selectionRect.x < x && x < selectionRect.x + (selectionRect.width-1);
var mY = Spectrum.height - selectionRect.y;
var inRectVertically = mY - (selectionRect.height-1) < y && y < mY;
return inRectHorizontally && inRectVertically;
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 00e6697ac3864607abf3f0d818c138ef
timeCreated: 1570031017

View file

@ -6,7 +6,7 @@ namespace cylvester
public class PdBackend : MonoBehaviour
{
public string mainPatch = "analyzer.pd";
public int samplePlayback = 0;
public int samplePlayback;
public PdArray levelMeterArray;
public IFftArrayContainer fftArrayContainer;
@ -14,8 +14,7 @@ namespace cylvester
private Action onSamplePlaybackChanged_;
private IPdSocket pdSocket_;
private void Start()
private void Awake()
{
PdProcess.Instance.Start(mainPatch);
levelMeterArray = new PdArray("levelmeters", PdConstant.NumMaxInputChannels);

View file

@ -1,25 +1,47 @@
using UnityEngine;
using UnityEngine.Events;
namespace cylvester
{
[System.Serializable]
class UnityFloatEvent : UnityEvent<float> { }
public interface IPdSpectrumBind
{
IPdArray GetPdArray(int index);
int Channel { get; set; }
int Energy { get; set; }
int Energy { get; }
int TextureWidth { get; }
int TextureHeight { get; }
Texture2D Spectrum { get; }
}
public class PdSpectrumBind : MonoBehaviour, IPdSpectrumBind
{
[SerializeField] private PdBackend pdBackend;
[SerializeField] private Rect selection;
[SerializeField] private UnityFloatEvent energyChanged;
public IPdArray GetPdArray(int index)
private ISpectrumGenerator spectrumGenerator_;
private void Start()
{
return pdBackend.fftArrayContainer[index];
var spectrumArray = pdBackend.fftArrayContainer[Channel];
spectrumGenerator_ = new SpectrumGeneratorPlayMode(TextureWidth, TextureHeight, spectrumArray);
}
public int TextureWidth { get; } = 512;
public int TextureHeight { get; } = 256;
public Texture2D Spectrum => spectrumGenerator_.Spectrum;
public int Channel { get; set; }
public int Energy { get; set; }
public int Energy { get; private set; }
private void Update()
{
var energy = spectrumGenerator_.Update(selection);
if (energy == Energy)
return;
Energy = energy;
energyChanged.Invoke(Energy);
}
}
}

View file

@ -0,0 +1,40 @@
using UnityEngine;
namespace cylvester
{
public class SpectrumGeneratorPlayMode : SpectrumGenerator, ISpectrumGenerator
{
private IPdArray pdArray_;
public SpectrumGeneratorPlayMode(int textureWidth, int textureHeight, IPdArray pdArray)
:base(textureWidth, textureHeight)
{
pdArray_ = pdArray;
}
public int Update( Rect selectionRect)
{
var numPixels = 0;
var data = pdArray_.Data;
OnAllPixels((x, y) =>
{
var magnitude = data[x] * 20f;
var validPixel = magnitude > y;
var color = validPixel ? Color.green : Color.black;
if (IsInSelection(x, y, ref selectionRect))
{
color.a = 1f;
if (validPixel)
numPixels++;
}
else
color.a = 0.2f;
Spectrum.SetPixel(x, y, color);
});
Spectrum.Apply();
return numPixels;
}
}
}