This commit is contained in:
h z
2024-06-21 20:57:19 +08:00
commit 7eed16cd02
48 changed files with 1599 additions and 0 deletions

31
Scenes/AddAtom.cs Executable file
View File

@@ -0,0 +1,31 @@
using Godot;
using VirtualChemistry.Constants;
public partial class AddAtom : MenuButton
{
public CompoundConstructor CompoundConstructor => GlobalScene.CompoundConstructor;
private void BuildMenu()
{
PopupMenu menu = GetPopup();
menu.Clear();
foreach (var w in menu.GetSignalConnectionList(PopupMenu.SignalName.IdPressed))
menu.Disconnect(PopupMenu.SignalName.IdPressed, w["callable"].AsCallable());
for (int i = 1; i <= 15; i++)
menu.AddItem(ChemistryConstant.ElementSymbols[i-1], i);
menu.Connect(PopupMenu.SignalName.IdPressed, Callable.From((int idx) => MenuHandle(idx)));
}
private void MenuHandle(int idx) => CompoundConstructor.AddAtomOf(idx);
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
BuildMenu();
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta)
{
}
}

31
Scenes/Atom.cs Executable file
View File

@@ -0,0 +1,31 @@
using Godot;
using VirtualChemistry.Chemistry.Atoms.Implements;
public partial class Atom : TextureRect
{
private Sprite2D ConnectPending { get; set; }
private Label PendingBond { get; set; }
public BaseAtom BaseAtom { get; set; } = BaseAtom.Null;
private Label ElementName { get; set; }
public AtomActions AtomActions { get; set; }
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
ElementName = GetNode<Label>("ElementName");
ElementName.Text = BaseAtom.Element;
ConnectPending = GetNode<Sprite2D>("ConnectPending");
PendingBond = GetNode<Label>("PendingBond");
AtomActions = GetNode<AtomActions>("AtomActions");
}
public override Variant _GetDragData(Vector2 atPosition)
{
return this;
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta)
{
}
}

41
Scenes/Atom.tscn Normal file
View File

@@ -0,0 +1,41 @@
[gd_scene load_steps=5 format=3 uid="uid://c6f1pohr81e62"]
[ext_resource type="Texture2D" uid="uid://dwdq5w17xjv18" path="res://Assets/Atom.png" id="1_8mgmq"]
[ext_resource type="Script" path="res://Scenes/Atom.cs" id="2_iicki"]
[ext_resource type="Script" path="res://Scenes/AtomActions.cs" id="3_3mce0"]
[ext_resource type="Texture2D" uid="uid://b5rdjefwmh10t" path="res://Assets/ConnectPendingAtom.png" id="4_l6xxo"]
[node name="Atom" type="TextureRect"]
offset_right = 64.0
offset_bottom = 64.0
texture = ExtResource("1_8mgmq")
script = ExtResource("2_iicki")
[node name="AtomActions" type="MenuButton" parent="."]
layout_mode = 0
offset_right = 64.0
offset_bottom = 64.0
button_mask = 2
script = ExtResource("3_3mce0")
[node name="ElementName" type="Label" parent="."]
layout_mode = 0
offset_left = 13.0
offset_top = 21.0
offset_right = 53.0
offset_bottom = 44.0
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_colors/font_outline_color = Color(0, 0.176471, 0.901961, 0.917647)
horizontal_alignment = 1
[node name="ConnectPending" type="Sprite2D" parent="."]
visible = false
position = Vector2(32, 32)
texture = ExtResource("4_l6xxo")
[node name="PendingBond" type="Label" parent="."]
layout_mode = 0
offset_left = 68.0
offset_top = 22.0
offset_right = 108.0
offset_bottom = 45.0

66
Scenes/AtomActions.cs Executable file
View File

@@ -0,0 +1,66 @@
using Godot;
using System.Collections.Generic;
using VirtualChemistry.Chemistry.Bonds.Implements;
public partial class AtomActions : MenuButton
{
public Atom Atom => GetParent<Atom>();
private Dictionary<int, BaseBond> BondMap { get; set; } = new();
private CompoundConstructor CompoundConstructor => GlobalScene.CompoundConstructor;
public void BuildMenu()
{
PopupMenu menu = GetPopup();
menu.Clear();
foreach (var w in menu.GetSignalConnectionList(PopupMenu.SignalName.IdPressed))
menu.Disconnect(PopupMenu.SignalName.IdPressed, w["callable"].AsCallable());
menu.AddItem("Remove Atom", 0);
int idx = 1;
BondMap.Clear();
foreach (BaseBond b in Atom.BaseAtom.UnconnectedBonds)
{
BondMap[idx] = b;
menu.AddItem(b.BondType, idx);
if(CompoundConstructor.ConnectPending == b)
menu.SetItemDisabled(idx, true);
idx++;
}
menu.Connect(PopupMenu.SignalName.IdPressed, Callable.From((int id) => MenuHandle(id)));
}
private void MenuHandle(int idx)
{
if (idx == 0)
{
CompoundConstructor.RemoveAtomOf(Atom);
return;
}
if (CompoundConstructor.ConnectPending == BaseBond.Null)
{
CompoundConstructor.ConnectPending = BondMap[idx];
CompoundConstructor.PendingAtom = Atom;
}
else
{
CompoundConstructor.ConnectAtoms(Atom, BondMap[idx]);
}
BuildMenu();
}
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
BuildMenu();
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta)
{
}
public override Variant _GetDragData(Vector2 atPosition)
{
return GetParent();
}
}

34
Scenes/Bond.cs Executable file
View File

@@ -0,0 +1,34 @@
using Godot;
public partial class Bond : Line2D
{
public Atom AtomFrom { get; set; }
public Atom AtomTo { get; set; }
private Label BondTypeA { get; set; }
private Label BondTypeB { get; set; }
public string TypeA { get; set; }
public string TypeB { get; set; }
public override void _Ready()
{
BondTypeA = GetNode<Label>("BondTypeA");
BondTypeB = GetNode<Label>("BondTypeB");
}
public void BondUpdate()
{
Vector2 v1 = AtomFrom.Position + new Vector2(32, 32);
Vector2 v2 = AtomTo.Position + new Vector2(32, 32);
Vector2 m = (v1 + v2) / 2;
Points = new[] { v1, m, v2 };
BondTypeA.Position = m - new Vector2(10, 10);
BondTypeB.Position = m + new Vector2(10, 10);
BondTypeA.Text = TypeA;
BondTypeB.Text = TypeB;
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta)
{
}
}

18
Scenes/Bond.tscn Normal file
View File

@@ -0,0 +1,18 @@
[gd_scene load_steps=2 format=3 uid="uid://ngc8dex7loqv"]
[ext_resource type="Script" path="res://Scenes/Bond.cs" id="1_rpgkt"]
[node name="Bond" type="Line2D"]
default_color = Color(1, 0.0392157, 1, 0.690196)
joint_mode = 2
begin_cap_mode = 2
end_cap_mode = 2
script = ExtResource("1_rpgkt")
[node name="BondTypeA" type="Label" parent="."]
offset_right = 40.0
offset_bottom = 23.0
[node name="BondTypeB" type="Label" parent="."]
offset_right = 40.0
offset_bottom = 23.0

92
Scenes/Bottle.cs Executable file
View File

@@ -0,0 +1,92 @@
using Godot;
using System.Linq;
using VirtualChemistry.Chemistry.Compounds.Implements;
using VirtualChemistry.Chemistry.Containers;
using VirtualChemistry.Chemistry.Mixtures.Implements;
public partial class Bottle : MenuButton, IChemicalContainer
{
private double ContainerVolume { get; set; }
private HeterogeneousMixture HeterogeneousMixture { get; set; }
public double Volume() => ContainerVolume;
public HeterogeneousMixture Content { get; set; }
public double EnvironmentPressure { get; set; }
public double EnvironmentTemperature { get; set; }
public HeterogeneousMixture ChemicalContent { get; set; } = HeterogeneousMixture.Null;
public HomogeneousMixture MainComponent => ChemicalContent.Layers
.DefaultIfEmpty(HomogeneousMixture.Null)
.MaxBy(x => x.Amount);
public Compound MainCompound => MainComponent.Compounds
.DefaultIfEmpty(Compound.Null)
.MaxBy(x => x.Amount);
private const string Empty = "Empty";
private string PotionName => $"Main: {(MainCompound == Compound.Null ? Empty : MainCompound.Expression)}";
private string PotionAmount => $"Amount: {(MainCompound == Compound.Null ? 0d : ChemicalContent.Amount)}";
public Sprite2D ContentTexture { get; set; }
private void BuildMenu()
{
PopupMenu menu = GetPopup();
menu.Clear();
foreach (var w in menu.GetSignalConnectionList(PopupMenu.SignalName.IdPressed))
menu.Disconnect(PopupMenu.SignalName.IdPressed, w["callable"].AsCallable());
menu.AddItem(PotionName, 0);
menu.AddItem(PotionAmount, 1);
menu.SetItemDisabled(0, true);
menu.SetItemDisabled(1, true);
menu.AddItem("Construct", 2);
menu.AddItem("Fill From Flask", 3);
menu.AddItem("Load From String", 4);
menu.AddItem("Add to Flask", 5);
menu.AddItem("Save to String", 6);
menu.AddItem("Discard", 7);
menu.Connect(PopupMenu.SignalName.IdPressed, Callable.From((int idx) => MenuHandle(idx)));
}
private void MenuHandle(int idx)
{
switch (idx)
{
case 2:
GlobalScene.Demo.SelectedBottle = this;
GlobalScene.MainScene.SwitchToConstructor();
return;
case 3:
ChemicalContent = GlobalScene.Flask.Content;
ChemicalContent.Container = this;
GlobalScene.Flask.Content = HeterogeneousMixture.Null;
GlobalScene.Flask.Update();
return;
case 4:
return;
default:
return;
}
}
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
ContentTexture = GetNode<Sprite2D>("Content");
if (ChemicalContent == HeterogeneousMixture.Null)
ContentTexture.Modulate = new Color(0, 0, 0, 0);
else
{
ContentTexture.Modulate = new Color(
MainComponent.ColorRed,
MainComponent.ColorGreen,
MainComponent.ColorBlue,
MainComponent.ColorTransparent
);
}
BuildMenu();
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta)
{
}
}

19
Scenes/Bottle.tscn Normal file
View File

@@ -0,0 +1,19 @@
[gd_scene load_steps=4 format=3 uid="uid://u7apjw1vvak3"]
[ext_resource type="Texture2D" uid="uid://dnfv1cab4fbb1" path="res://Assets/Bottle.png" id="1_ghgoa"]
[ext_resource type="Script" path="res://Scenes/Bottle.cs" id="1_i76fj"]
[ext_resource type="Texture2D" uid="uid://daq4xhxh3x5s8" path="res://Assets/Content.png" id="2_s0bcd"]
[node name="Bottle" type="MenuButton"]
custom_minimum_size = Vector2(64, 64)
offset_right = 64.0
offset_bottom = 64.0
script = ExtResource("1_i76fj")
[node name="Bottle" type="Sprite2D" parent="."]
position = Vector2(34, 32)
texture = ExtResource("1_ghgoa")
[node name="Content" type="Sprite2D" parent="."]
position = Vector2(34, 32)
texture = ExtResource("2_s0bcd")

146
Scenes/CompoundConstructor.cs Executable file
View File

@@ -0,0 +1,146 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Godot;
using VirtualChemistry.Chemistry.Atoms.Implements;
using VirtualChemistry.Chemistry.Atoms.Resolver;
using VirtualChemistry.Chemistry.Bonds.Implements;
using VirtualChemistry.Chemistry.Compounds.Implements;
using VirtualChemistry.Chemistry.Mixtures.Implements;
public partial class CompoundConstructor : Panel
{
private HeterogeneousMixture HeterogeneousMixture { get; set; }
public HomogeneousMixture HomogeneousMixture { get; set; }
public HashSet<Atom> Atoms { get; set; } = new();
public BaseBond ConnectPending { get; set; } = BaseBond.Null;
public Atom PendingAtom { get; set; }
public Dictionary<Atom, HashSet<Bond>> BondMap { get; set; } = new();
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
GlobalScene.CompoundConstructor = this;
HeterogeneousMixture = new();
HomogeneousMixture = HeterogeneousMixture.AddLayer();
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta)
{
}
public override bool _CanDropData(Vector2 atPosition, Variant data)
{
return true;
}
public override void _DropData(Vector2 atPosition, Variant data)
{
Atom a = data.As<Atom>();
a.Position = atPosition;
if(BondMap.ContainsKey(a))
foreach (Bond b in BondMap[a])
b.BondUpdate();
}
public void AddAtomOf(int idx)
{
BaseAtom a = AtomResolver.Resolve(idx);
Compound c = a.GrabCompound;
c.Amount = 1;
Atom at = ResourceLoader.Load<PackedScene>("res://Scenes/Atom.tscn").Instantiate<Atom>();
at.BaseAtom = a;
AddChild(at);
at.Position = new(500, 500);
HomogeneousMixture.AddCompound(c);
Atoms.Add(at);
}
public void RemoveAtomOf(Atom atom)
{
foreach (BaseBond b in atom.BaseAtom.ConnectedBonds)
b.Disconnect();
Bond[] toRemove = Array.Empty<Bond>();
if (BondMap.TryGetValue(atom, out HashSet<Bond> value))
toRemove = value.ToArray();
foreach (Bond b in toRemove)
{
BondMap[b.AtomFrom].Remove(b);
BondMap[b.AtomTo].Remove(b);
RemoveChild(b);
}
atom.BaseAtom.Compound.HomogeneousMixture.Compounds.Remove(atom.BaseAtom.Compound);
atom.BaseAtom.Compound.HomogeneousMixture = HomogeneousMixture.Null;
atom.BaseAtom.Compound = Compound.Null;
RemoveChild(atom);
Atoms.Remove(atom);
}
public void ConnectAtoms(Atom a, BaseBond b)
{
b.Connect(ConnectPending);
Bond bond = ResourceLoader.Load<PackedScene>("res://Scenes/Bond.tscn").Instantiate<Bond>();
bond.AtomFrom = PendingAtom;
bond.AtomTo = a;
bond.TypeA = ConnectPending.BondType;
bond.TypeB = b.BondType;
if (!BondMap.ContainsKey(a))
BondMap[a] = new HashSet<Bond>();
BondMap[a].Add(bond);
if (!BondMap.ContainsKey(PendingAtom!))
BondMap[PendingAtom] = new HashSet<Bond>();
BondMap[PendingAtom].Add(bond);
ConnectPending = BaseBond.Null;
a.AtomActions.BuildMenu();
PendingAtom.AtomActions.BuildMenu();
PendingAtom = null;
AddChild(bond);
bond.BondUpdate();
}
private void Back()
{
GlobalScene.MainScene.SwitchToDemo();
}
private void Build()
{
HeterogeneousMixture.Container = GlobalScene.Demo.SelectedBottle;
GlobalScene.Demo.SelectedBottle.Content = HeterogeneousMixture;
Clear();
}
private void Clear(bool save = true)
{
if (!save)
{
foreach (Atom atom in Atoms)
RemoveAtomOf(atom);
return;
}
HashSet<Bond> removedBonds = new();
foreach (Atom atom in Atoms)
{
RemoveChild(atom);
foreach (Bond bond in BondMap[atom])
{
if (!removedBonds.Contains(bond))
RemoveChild(bond);
removedBonds.Add(bond);
}
}
Atoms = new();
BondMap = new();
HeterogeneousMixture = new HeterogeneousMixture();
}
private void ForceClear() => Clear(false);
}

View File

@@ -0,0 +1,43 @@
[gd_scene load_steps=3 format=3 uid="uid://c1ww4cmceq16t"]
[ext_resource type="Script" path="res://Scenes/CompoundConstructor.cs" id="1_bhwhs"]
[ext_resource type="Script" path="res://Scenes/AddAtom.cs" id="2_ddlyc"]
[node name="CompoundConstructor" type="Panel"]
offset_left = -370.0
offset_top = -182.0
offset_right = 1678.0
offset_bottom = 842.0
script = ExtResource("1_bhwhs")
[node name="ControlLayer" type="CanvasLayer" parent="."]
[node name="Controller" type="HBoxContainer" parent="ControlLayer"]
offset_left = 57.0
offset_top = 588.0
offset_right = 877.0
offset_bottom = 632.0
[node name="AddAtom" type="MenuButton" parent="ControlLayer/Controller"]
layout_mode = 2
text = "Add Atom ^"
script = ExtResource("2_ddlyc")
[node name="BuildMixture" type="Button" parent="ControlLayer/Controller"]
layout_mode = 2
text = "Build
"
[node name="Clear" type="Button" parent="ControlLayer/Controller"]
layout_mode = 2
text = "Clear"
[node name="Back" type="Button" parent="ControlLayer/Controller"]
layout_mode = 2
text = "Back"
[node name="MovingLayer" type="CanvasLayer" parent="."]
[connection signal="pressed" from="ControlLayer/Controller/BuildMixture" to="." method="Build"]
[connection signal="pressed" from="ControlLayer/Controller/Clear" to="." method="ForceClear"]
[connection signal="pressed" from="ControlLayer/Controller/Back" to="." method="Back"]

60
Scenes/Flask.cs Executable file
View File

@@ -0,0 +1,60 @@
using Godot;
using Mathool.DataStructure;
using VirtualChemistry.Chemistry.Containers;
using VirtualChemistry.Chemistry.Mixtures.Implements;
public partial class Flask : TextureRect, IChemicalContainer
{
public double ContainerVolume { get; set; } = 100;
public double Volume() => ContainerVolume;
public HeterogeneousMixture Content { get; set; }
public double EnvironmentPressure { get; set; }
public double EnvironmentTemperature { get; set; }
public IsomorphicMap<HomogeneousMixture, FlaskContent> Map { get; set; } = new();
private void BuildMap()
{
foreach (HomogeneousMixture m in Content.Layers)
{
if (!Map.ContainsKey(m))
{
FlaskContent fc = ResourceLoader
.Load<PackedScene>("res://Scenes/FlaskContent.tscn")
.Instantiate<FlaskContent>();
fc.Mixture = m;
AddChild(fc);
fc.Position = new(0, -(float)fc.StartFrom);
}
}
foreach (FlaskContent fc in Map.Values)
{
if (fc.Mixture.HeterogeneousMixture == HeterogeneousMixture.Null)
{
Map.Remove(fc);
RemoveChild(fc);
}
}
}
public void Update()
{
BuildMap();
foreach (FlaskContent fc in Map.Values)
fc.UpdateVolumeAndPosition();
}
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
GlobalScene.Flask = this;
Content = HeterogeneousMixture.Null;
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta)
{
}
}

10
Scenes/Flask.tscn Normal file
View File

@@ -0,0 +1,10 @@
[gd_scene load_steps=3 format=3 uid="uid://bde5n0vfm6jvs"]
[ext_resource type="Texture2D" uid="uid://chx2yte05x4lj" path="res://Assets/Flask.png" id="1_b34pm"]
[ext_resource type="Script" path="res://Scenes/Flask.cs" id="2_j1pe5"]
[node name="Flask" type="TextureRect"]
offset_right = 40.0
offset_bottom = 40.0
texture = ExtResource("1_b34pm")
script = ExtResource("2_j1pe5")

65
Scenes/FlaskContent.cs Executable file
View File

@@ -0,0 +1,65 @@
using Godot;
using Mathool.DataStructure.Link;
using VirtualChemistry.Chemistry.Mixtures.Implements;
public partial class FlaskContent : ColorRect
{
public HomogeneousMixture Mixture { get; set; } = HomogeneousMixture.Null;
private const double ContainerHeight = 512d;
public double ContainerVolume => Mixture.HeterogeneousMixture.Container.Volume();
public double Volume => Mixture.Volume;
private TextureButton Mask { get; set; }
public double VolumeStartFrom {
get
{
double res = 0;
for (LinkNode<HomogeneousMixture> iter = Mixture.Layer.Previous; !iter.IsEnding; iter = iter.Previous)
res += iter.Value.Volume;
return res;
}
}
private ColorRect Bar { get; set; }
private AnimationPlayer AP { get; set; }
public double StartFrom => (VolumeStartFrom / ContainerVolume) * ContainerHeight;
public void UpdateVolumeAndPosition()
{
double height = (Volume / ContainerVolume) * ContainerHeight;
Animation a = AP.GetAnimation("HeightChange");
a.TrackSetKeyValue(0, 0, Size);
a.TrackSetKeyValue(0, 1, new Vector2(Size.X, (float)height));
AP.Play("HeightChange");
AP.Play("PositionChange");
a = AP.GetAnimation("PositionChange");
a.TrackSetKeyValue(0,0,Position);
a.TrackSetKeyValue(0,1, new Vector2(Position.X, -(float)StartFrom));
AP.Play();
}
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
Bar = GetNode<ColorRect>("Bar");
AP = GetNode<AnimationPlayer>("AP");
Mask = GetNode<TextureButton>("Mask");
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta)
{
}
private void OnSizeChange() => Mask.Size = Size;
private void OnMouseEnter() => AP.Play("MouseEntered");
private void OnMouseExit() => AP.PlayBackwards("MouseEntered");
private void OnClick()
{
GlobalScene.MainControlPanel.Update(Mixture);
}
}

84
Scenes/FlaskContent.tscn Normal file
View File

@@ -0,0 +1,84 @@
[gd_scene load_steps=6 format=3 uid="uid://bvn25sn10au02"]
[ext_resource type="Script" path="res://Scenes/FlaskContent.cs" id="1_4wcwd"]
[sub_resource type="Animation" id="Animation_04bwn"]
resource_name = "HeightChange"
length = 0.5
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath(".:size")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 0.4995),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [Vector2(128, 4), Vector2(128, 4)]
}
[sub_resource type="Animation" id="Animation_itbox"]
resource_name = "MouseEntered"
length = 0.15
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Bar:size")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 0.1332),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [Vector2(150, 2), Vector2(170, 2)]
}
[sub_resource type="Animation" id="Animation_qjpxn"]
resource_name = "PositionChange"
length = 0.5
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath(".:position")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 0.4995),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [Vector2(0, 0), Vector2(0, 0)]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_5ty26"]
_data = {
"HeightChange": SubResource("Animation_04bwn"),
"MouseEntered": SubResource("Animation_itbox"),
"PositionChange": SubResource("Animation_qjpxn")
}
[node name="FlaskContent" type="ColorRect"]
custom_minimum_size = Vector2(128, 0)
offset_right = 128.0
offset_bottom = 4.0
script = ExtResource("1_4wcwd")
[node name="Bar" type="ColorRect" parent="."]
layout_mode = 0
offset_right = 170.0
offset_bottom = 2.0
color = Color(0.160784, 0, 0, 0.701961)
[node name="AP" type="AnimationPlayer" parent="."]
libraries = {
"": SubResource("AnimationLibrary_5ty26")
}
[node name="Mask" type="TextureButton" parent="."]
layout_mode = 0
offset_right = 128.0
offset_bottom = 4.0
[connection signal="resized" from="." to="." method="OnSizeChange"]
[connection signal="mouse_entered" from="Mask" to="." method="OnMouseEnter"]
[connection signal="mouse_exited" from="Mask" to="." method="OnMouseExit"]

View File

@@ -0,0 +1,80 @@
using Godot;
using System.Linq;
using VirtualChemistry.Chemistry.Compounds.Implements;
using VirtualChemistry.Chemistry.Mixtures.Implements;
public partial class MainControlPanel : HBoxContainer
{
private HSlider EnvTemperature { get; set; }
private HSlider ContainerVolume { get; set; }
private HomogeneousMixture SelectedMixture { get; set; }
private Label Density { get; set; }
private Label Volume { get; set; }
private Label HeatConductivity { get; set; }
private Label Amount { get; set; }
private Label Temperature { get; set; }
private Tree Compounds { get; set; }
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
EnvTemperature = GetNode<HSlider>("V1/EnvTemperature");
ContainerVolume = GetNode<HSlider>("V1/ContainerVolume");
Density = GetNode<Label>("V2/HDensity/Density");
Volume = GetNode<Label>("V2/HVolume/Volume");
HeatConductivity = GetNode<Label>("V2/HHeatConductivity/HeatConductivity");
Amount = GetNode<Label>("V2/HAmount/Amount");
Temperature = GetNode<Label>("V2/HTemperature/Temperature");
Compounds = GetNode<Tree>("V3/Compounds");
GlobalScene.MainControlPanel = this;
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta)
{
}
public void Update(HomogeneousMixture mixture)
{
SelectedMixture = mixture;
}
public void Update()
{
if (SelectedMixture == HomogeneousMixture.Null)
return;
Density.Text = $"{SelectedMixture.Density}";
Volume.Text = $"{SelectedMixture.Volume}";
HeatConductivity.Text = $"{SelectedMixture.HeatConductivity}";
Amount.Text = $"{SelectedMixture.Amount}";
Temperature.Text = $"{SelectedMixture.Temperature}";
}
private void BuildTree()
{
Compounds.Clear();
var groups = SelectedMixture.Compounds.GroupBy(x => x.Expression);
foreach (var group in groups)
{
TreeItem t = Compounds.CreateItem();
t.SetText(0, group.Key);
t.SetSelectable(0, false);
int i = 1;
foreach (Compound c in group)
{
TreeItem x = Compounds.CreateItem(t);
x.SetText(0, $"{i}th isomer");
x.SetMeta("isoExp", c.IsoRepresentation);
}
}
}
private void IsomerSelectedHandler()
{
GlobalScene.Demo.FullRepr.Text = Compounds
.GetSelected()
.GetMeta("isoExp")
.AsString();
}
}

View File

@@ -0,0 +1,123 @@
[gd_scene load_steps=2 format=3 uid="uid://re3bwidsx0do"]
[ext_resource type="Script" path="res://Scenes/MainControlPanel.cs" id="1_r6pkl"]
[node name="MainControlPanel" type="HBoxContainer"]
script = ExtResource("1_r6pkl")
[node name="VSeparator3" type="VSeparator" parent="."]
layout_mode = 2
[node name="V1" type="VBoxContainer" parent="."]
layout_mode = 2
[node name="OneStep" type="Button" parent="V1"]
layout_mode = 2
text = "One Step
"
[node name="RemoveTop" type="Button" parent="V1"]
layout_mode = 2
text = "Remove Top"
[node name="RemoveBottom" type="Button" parent="V1"]
layout_mode = 2
text = "Remove Bottom"
[node name="Label" type="Label" parent="V1"]
layout_mode = 2
text = "Env Temperature"
[node name="EnvTemperature" type="HSlider" parent="V1"]
layout_mode = 2
[node name="Label2" type="Label" parent="V1"]
layout_mode = 2
text = "Container Volume"
[node name="ContainerVolume" type="HSlider" parent="V1"]
layout_mode = 2
[node name="VSeparator" type="VSeparator" parent="."]
layout_mode = 2
[node name="V2" type="VBoxContainer" parent="."]
layout_mode = 2
[node name="Label" type="Label" parent="V2"]
layout_mode = 2
text = "Selected Mixture Measures"
[node name="HDensity" type="HBoxContainer" parent="V2"]
layout_mode = 2
[node name="Label" type="Label" parent="V2/HDensity"]
layout_mode = 2
text = "Density: "
[node name="Density" type="Label" parent="V2/HDensity"]
layout_mode = 2
text = "0"
[node name="HVolume" type="HBoxContainer" parent="V2"]
layout_mode = 2
[node name="Label" type="Label" parent="V2/HVolume"]
layout_mode = 2
text = "Volume: "
[node name="Volume" type="Label" parent="V2/HVolume"]
layout_mode = 2
text = "0"
[node name="HHeatConductivity" type="HBoxContainer" parent="V2"]
layout_mode = 2
[node name="Label" type="Label" parent="V2/HHeatConductivity"]
layout_mode = 2
text = "Heat Conductivity: "
[node name="HeatConductivity" type="Label" parent="V2/HHeatConductivity"]
layout_mode = 2
text = "0"
[node name="HAmount" type="HBoxContainer" parent="V2"]
layout_mode = 2
[node name="Label" type="Label" parent="V2/HAmount"]
layout_mode = 2
text = "Amount: "
[node name="Amount" type="Label" parent="V2/HAmount"]
layout_mode = 2
text = "0"
[node name="HTemperature" type="HBoxContainer" parent="V2"]
layout_mode = 2
[node name="Label" type="Label" parent="V2/HTemperature"]
layout_mode = 2
text = "Temperature: "
[node name="Temperature" type="Label" parent="V2/HTemperature"]
layout_mode = 2
text = "0"
[node name="VSeparator2" type="VSeparator" parent="."]
layout_mode = 2
[node name="V3" type="VBoxContainer" parent="."]
layout_mode = 2
[node name="Label" type="Label" parent="V3"]
layout_mode = 2
text = "Compounents"
[node name="Compounds" type="Tree" parent="V3"]
custom_minimum_size = Vector2(150, 200)
layout_mode = 2
[node name="VSeparator4" type="VSeparator" parent="."]
layout_mode = 2
[connection signal="item_selected" from="V3/Compounds" to="." method="IsomerSelectedHandler"]

46
Scenes/MainScene.cs Normal file
View File

@@ -0,0 +1,46 @@
using Godot;
using System;
public partial class MainScene : Node2D
{
public Node CurrentScene { get; set; }
public VirtualChemDemo Demo { get; set; }
public override void _Ready()
{
GlobalScene.MainScene = this;
Demo = GetNode<VirtualChemDemo>("Demo");
CurrentScene = Demo;
}
public void SwitchToConstructor()
{
if (CurrentScene == GlobalScene.CompoundConstructor)
return;
if (CurrentScene != Demo)
throw new Exception("?");
if (GlobalScene.CompoundConstructor == null)
GlobalScene.CompoundConstructor = ResourceLoader
.Load<PackedScene>("res://Scenes/CompoundConstructor.tscn")
.Instantiate<CompoundConstructor>();
RemoveChild(Demo);
AddChild(GlobalScene.CompoundConstructor);
CurrentScene = GlobalScene.CompoundConstructor;
}
public void SwitchToDemo()
{
if (CurrentScene == Demo)
return;
if (CurrentScene != GlobalScene.CompoundConstructor)
throw new Exception("?");
RemoveChild(CurrentScene);
AddChild(Demo);
CurrentScene = Demo;
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta)
{
}
}

9
Scenes/MainScene.tscn Normal file
View File

@@ -0,0 +1,9 @@
[gd_scene load_steps=3 format=3 uid="uid://cgdqxc2mx6w8l"]
[ext_resource type="Script" path="res://Scenes/MainScene.cs" id="1_uwony"]
[ext_resource type="PackedScene" uid="uid://br78rurjakp3a" path="res://Scenes/VirtualChemDemo.tscn" id="2_vpu75"]
[node name="MainScene" type="Node2D"]
script = ExtResource("1_uwony")
[node name="Demo" parent="." instance=ExtResource("2_vpu75")]

35
Scenes/StringLoader.cs Normal file
View File

@@ -0,0 +1,35 @@
using Godot;
using VirtualChemistry.Chemistry.Mixtures.Implements;
using VirtualChemistry.Chemistry.Mixtures.Resolver;
public partial class StringLoader : Window
{
public TextEdit Input { get; set; }
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
Input = GetNode<TextEdit>("Input");
GlobalScene.StringLoader = this;
}
private void Load()
{
string expression = Input.Text;
HeterogeneousMixture mixture = HeterogeneousMixtureResolver.Resolve(expression);
GlobalScene.Demo.SelectedBottle.Content = mixture;
mixture.Container = GlobalScene.Demo.SelectedBottle;
Visible = false;
}
private void Cancel()
{
Visible = false;
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta)
{
}
}

43
Scenes/StringLoader.tscn Normal file
View File

@@ -0,0 +1,43 @@
[gd_scene load_steps=2 format=3 uid="uid://cubdalatqln4x"]
[ext_resource type="Script" path="res://Scenes/StringLoader.cs" id="1_qoxxp"]
[node name="StringLoader" type="Window"]
size = Vector2i(500, 250)
script = ExtResource("1_qoxxp")
[node name="VBoxContainer" type="VBoxContainer" parent="."]
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -225.0
offset_top = -37.5
offset_right = 225.0
offset_bottom = 37.5
grow_horizontal = 2
grow_vertical = 2
[node name="Label" type="Label" parent="VBoxContainer"]
layout_mode = 2
text = "Expression:"
[node name="Input" type="TextEdit" parent="VBoxContainer"]
custom_minimum_size = Vector2(450, 40)
layout_mode = 2
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
layout_mode = 2
alignment = 1
[node name="Load" type="Button" parent="VBoxContainer/HBoxContainer"]
layout_mode = 2
text = "Load"
[node name="Cancel" type="Button" parent="VBoxContainer/HBoxContainer"]
layout_mode = 2
text = "Cancel"
[connection signal="pressed" from="VBoxContainer/HBoxContainer/Load" to="." method="Load"]
[connection signal="pressed" from="VBoxContainer/HBoxContainer/Cancel" to="." method="Cancel"]

30
Scenes/VirtualChemDemo.cs Executable file
View File

@@ -0,0 +1,30 @@
using Godot;
public partial class VirtualChemDemo : Node2D
{
public Flask Flask { get; set; }
public RichTextLabel FullRepr { get; set; }
public Bottle SelectedBottle { get; set; }
private StringLoader ExpressionLoader { get; set; }
public void OneStep()
{
Flask.Content.OneStep();
Flask.Update();
}
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
ExpressionLoader = GetNode<StringLoader>("ExpressionLoader");
ExpressionLoader.Visible = false;
FullRepr = GetNode<RichTextLabel>("HRepr/FullRepr");
Flask = GetNode<Flask>("Flask");
GlobalScene.Demo = this;
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta)
{
}
}

View File

@@ -0,0 +1,87 @@
[gd_scene load_steps=6 format=3 uid="uid://br78rurjakp3a"]
[ext_resource type="Script" path="res://Scenes/VirtualChemDemo.cs" id="1_aartk"]
[ext_resource type="PackedScene" uid="uid://re3bwidsx0do" path="res://Scenes/MainControlPanel.tscn" id="2_cfpbe"]
[ext_resource type="PackedScene" uid="uid://u7apjw1vvak3" path="res://Scenes/Bottle.tscn" id="2_ei0nr"]
[ext_resource type="PackedScene" uid="uid://bde5n0vfm6jvs" path="res://Scenes/Flask.tscn" id="3_p6s6c"]
[ext_resource type="PackedScene" uid="uid://cubdalatqln4x" path="res://Scenes/StringLoader.tscn" id="5_5y6ld"]
[node name="VicrtualChemDemo" type="Node2D"]
script = ExtResource("1_aartk")
[node name="VBoxContainer" type="VBoxContainer" parent="."]
offset_right = 40.0
offset_bottom = 40.0
[node name="HSeparator" type="HSeparator" parent="VBoxContainer"]
layout_mode = 2
[node name="MainControlPanel" parent="VBoxContainer" instance=ExtResource("2_cfpbe")]
layout_mode = 2
[node name="HSeparator2" type="HSeparator" parent="VBoxContainer"]
layout_mode = 2
[node name="Potions" type="HBoxContainer" parent="."]
offset_left = 70.0
offset_top = 553.0
offset_right = 670.0
offset_bottom = 621.0
[node name="Prev" type="Button" parent="Potions"]
layout_mode = 2
text = "<-"
[node name="Items" type="HBoxContainer" parent="Potions"]
layout_mode = 2
[node name="MenusButton" parent="Potions/Items" instance=ExtResource("2_ei0nr")]
layout_mode = 2
[node name="Bottle" parent="Potions/Items" instance=ExtResource("2_ei0nr")]
layout_mode = 2
[node name="Bottle2" parent="Potions/Items" instance=ExtResource("2_ei0nr")]
layout_mode = 2
[node name="Bottle3" parent="Potions/Items" instance=ExtResource("2_ei0nr")]
layout_mode = 2
[node name="Bottle4" parent="Potions/Items" instance=ExtResource("2_ei0nr")]
layout_mode = 2
[node name="Bottle5" parent="Potions/Items" instance=ExtResource("2_ei0nr")]
layout_mode = 2
[node name="Bottle6" parent="Potions/Items" instance=ExtResource("2_ei0nr")]
layout_mode = 2
[node name="Bottle7" parent="Potions/Items" instance=ExtResource("2_ei0nr")]
layout_mode = 2
[node name="Next" type="Button" parent="Potions"]
layout_mode = 2
text = "->"
[node name="Flask" parent="." instance=ExtResource("3_p6s6c")]
offset_left = 747.0
offset_top = 57.0
offset_right = 875.0
offset_bottom = 569.0
[node name="HRepr" type="HBoxContainer" parent="."]
offset_left = 32.0
offset_top = 272.0
offset_right = 702.0
offset_bottom = 352.0
[node name="Label" type="Label" parent="HRepr"]
layout_mode = 2
text = "Full Expression"
[node name="FullRepr" type="RichTextLabel" parent="HRepr"]
custom_minimum_size = Vector2(550, 80)
layout_mode = 2
[node name="ExpressionLoader" parent="." instance=ExtResource("5_5y6ld")]
position = Vector2i(250, 200)