Skip to content

Commit 8e44ce0

Browse files
committed
Big Update: Graphics (Part 1)
1 parent 9a5ac20 commit 8e44ce0

19 files changed

Lines changed: 682 additions & 18 deletions

File tree

.editorconfig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[*.cs]
2+
3+
# IDE0160: Convert to block scoped namespace
4+
csharp_style_namespace_declarations = block_scoped

PaintPower.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@
3838
<PackageReference Include="Classic.Avalonia.Theme.ColorPicker" Version="11.3.0.3" />
3939
<PackageReference Include="Classic.CommonControls.Avalonia" Version="11.3.0.3" />
4040
<PackageReference Include="NAudio" Version="2.3.0" />
41+
<PackageReference Include="SixLabors.ImageSharp" Version="3.0.2" />
42+
<PackageReference Include="Svg.Skia" Version="1.0.0" />
43+
44+
4145
</ItemGroup>
4246

4347
<!-- Build timestamp -->
Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,30 @@
11
using System;
2+
using PaintPower.Tools.Graphics;
23

34
namespace PaintPower.Display.DisplayIntegration;
45

5-
public class DIItem
6+
public abstract class DIItem
67
{
7-
public DIItem() {}
8+
public double? x;
9+
public double? y;
10+
11+
public float ScaleX = 1f;
12+
public float ScaleY = 1f;
13+
14+
public bool IsVisible = true;
15+
16+
public float Scale
17+
{
18+
get => ScaleX;
19+
set { ScaleX = value; ScaleY = value; }
20+
}
21+
22+
public int Z = 0;
23+
24+
public float Rotation = 0f; // 90 is normal, goes from 0 - 360.
25+
26+
public DIItem() { }
27+
28+
public abstract object DrawAs();
29+
830
}
Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,126 @@
11
using System;
2+
using System.Collections.Generic;
3+
using PaintPower.Tools;
4+
using PaintPower.Tools.Graphics;
25

36
namespace PaintPower.Display.DisplayIntegration;
47

8+
// DIPlay or Diplay. You know, like Dip-lay, like "Display" with out the 's'. (Inspired by a typo while learning CSS)
59
public class DIPlay
610
{
7-
public DIPlay() {}
11+
public GfxPane gfxPane;
12+
public static Point stageSize = new(PaintPower_Engine.App._project?.Metadata?.StageWidth, PaintPower_Engine.App._project?.Metadata?.StageHeight);
13+
14+
public List<DIItem> items = new();
15+
16+
public DIPlay()
17+
{
18+
setStageSize();
19+
gfxPane = new(stageSize.x, stageSize.y);
20+
}
21+
22+
public void setStageSize()
23+
{
24+
stageSize = new(PaintPower_Engine.App._project?.Metadata?.StageWidth, PaintPower_Engine.App._project?.Metadata?.StageHeight);
25+
}
26+
27+
public void Start()
28+
{
29+
var timer = new System.Timers.Timer(1000.0 / 60.0);
30+
timer.Elapsed += async (_, __) => Tick();
31+
timer.Start();
32+
}
33+
34+
public async void Tick()
35+
{
36+
var pane = gfxPane;
37+
int t = 0;
38+
39+
await PaintPower_Engine.App.vm.Tick();
40+
41+
List<DrawCommand> batch = new();
42+
43+
foreach (DIItem item in items)
44+
{
45+
if (!item.IsVisible)
46+
continue;
47+
48+
var g = item.DrawAs();
49+
50+
if (g is GraphicAnimation anim)
51+
{
52+
int frame = ResolveAnimationFrame(anim, t * 16);
53+
Graphic frameGraphic = anim.Frames[frame];
54+
55+
if (IsCulled(frameGraphic, (float)item.x, (float)item.y, item.ScaleX, item.ScaleY))
56+
continue;
57+
58+
batch.Add(new DrawCommand(
59+
frameGraphic,
60+
(float)item.x,
61+
(float)item.y,
62+
item.Rotation,
63+
item.ScaleX,
64+
item.ScaleY,
65+
item.Z
66+
));
67+
68+
}
69+
else if (g is Graphic img)
70+
{
71+
if (IsCulled(img, (float)item.x, (float)item.y, item.ScaleX, item.ScaleY))
72+
continue;
73+
74+
batch.Add(new DrawCommand(
75+
img,
76+
(float)item.x,
77+
(float)item.y,
78+
item.Rotation,
79+
item.ScaleX,
80+
item.ScaleY,
81+
item.Z
82+
));
83+
}
84+
}
85+
86+
// Sort for Z-Layering
87+
batch.Sort((a, b) => a.Z.CompareTo(b.Z));
88+
89+
pane.Renderer.Clear(0xFF202020);
90+
pane.Renderer.DrawBatch(batch);
91+
92+
Avalonia.Threading.Dispatcher.UIThread.Post(pane.Present);
93+
94+
t++;
95+
}
96+
97+
private bool IsCulled(Graphic g, float x, float y, float scaleX, float scaleY)
98+
{
99+
float halfW = g.Width * scaleX / 2f;
100+
float halfH = g.Height * scaleY / 2f;
101+
102+
float left = x - halfW;
103+
float right = x + halfW;
104+
float top = y - halfH;
105+
float bottom = y + halfH;
106+
107+
return right < 0 ||
108+
left > stageSize.x ||
109+
bottom < 0 ||
110+
top > stageSize.y;
111+
}
112+
113+
private int ResolveAnimationFrame(GraphicAnimation anim, int timeMs)
114+
{
115+
int total = 0;
116+
117+
for (int i = 0; i < anim.FrameDelays.Count; i++)
118+
{
119+
total += anim.FrameDelays[i];
120+
if (timeMs % total < anim.FrameDelays[i])
121+
return i;
122+
}
123+
124+
return 0;
125+
}
8126
}

src/Display/Stage/StageItem.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
using System;
2+
using PaintPower.Sprites;
23

34
namespace PaintPower.Display.Stage;
45

5-
public class StageItem
6+
public class StageItem : Sprite
67
{
78
public StageItem() {}
89
}

src/PaintPower_Engine.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -573,7 +573,7 @@ public async void SaveToServer()
573573
}
574574

575575
// 2. Project not linked → ask to link
576-
if (!project.Metadata.IsLinked)
576+
if (!project.Metadata.IsLinked())
577577
{
578578
var linkDialog = new LinkBeforeUploadDialog();
579579
var linkChoice = await linkDialog.ShowDialog<string>(MainWindow.window);
@@ -610,7 +610,7 @@ public async void SaveToServer()
610610
}
611611

612612
// 3. Project is linked → ask overwrite/unlink
613-
if (project.Metadata.IsLinked)
613+
if (project.Metadata.IsLinked())
614614
{
615615
var uploadDialog = new UploadOptionsDialog(project.Metadata.serverId!);
616616
var choice = await uploadDialog.ShowDialog<string>(MainWindow.window);

src/ProjectSystem/PaintProject.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public async Task Load(string projectPath)
113113
Avalonia.Threading.Dispatcher.UIThread.Post(() =>
114114
{
115115

116-
if (PaintPower_Engine.App.server.Username != "" && !Metadata.IsLinked)
116+
if (PaintPower_Engine.App.server.Username != "" && Metadata.IsLinked())
117117
{
118118
PaintPower_Engine.App.AskToLinkProject(this);
119119
}
@@ -203,7 +203,14 @@ public class ProjectMetadata
203203
public string? name { get; set; } = "Untitled Project";
204204
public string? OpenFile { get; set; }
205205

206+
public double? StageWidth { get; set; } = 640;
207+
public double? StageHeight { get; set; } = 450;
208+
206209
// For online options.
207-
public string? serverId { get; set; }
208-
public bool IsLinked => !string.IsNullOrEmpty(serverId);
210+
public string? serverId { get; set; } = null;
211+
public bool IsLinked()
212+
{
213+
if (serverId == "0") return false;
214+
return !string.IsNullOrEmpty(serverId);
215+
}
209216
}

src/Sprites/Skin.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System;
2+
using System.IO;
3+
using System.IO.Compression;
4+
using PaintPower.Logging;
5+
6+
namespace PaintPower.Sprites;
7+
8+
public class Skin
9+
{
10+
public string? path = "";
11+
12+
// Path to image.
13+
public Skin(string path)
14+
{
15+
this.path = path;
16+
}
17+
}

src/Sprites/Sprite.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System;
2+
using PaintPower.Logging;
3+
using PaintPower.Display.Stage;
4+
using PaintPower.Display.DisplayIntegration;
5+
using PaintPower.Tools.Graphics;
6+
7+
namespace PaintPower.Sprites;
8+
9+
public class Sprite : DIItem
10+
{
11+
public Skin? skin;
12+
13+
public Sprite(double? x = null, double? y = null, Skin? skin = null)
14+
{
15+
if (x != null) this.x = x; else this.x = Tools.Math.Random.calc(DIPlay.stageSize.x, DIPlay.stageSize.y);
16+
if (y != null) this.y = y;
17+
if (skin != null) this.skin = skin;
18+
}
19+
20+
public void SetSkin(Skin skin)
21+
{
22+
this.skin = skin;
23+
}
24+
25+
public override object DrawAs()
26+
{
27+
return Graphic.ToGraphic(skin);
28+
}
29+
30+
}

src/Tools/Graphics/DrawCommand.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System;
2+
3+
namespace PaintPower.Tools.Graphics;
4+
5+
public struct DrawCommand
6+
{
7+
public Graphic Graphic;
8+
public float X;
9+
public float Y;
10+
public float Rotation;
11+
public float ScaleX;
12+
public float ScaleY;
13+
public int Z;
14+
15+
public DrawCommand(Graphic g, float x, float y, float rot, float sx, float sy, int z)
16+
{
17+
Graphic = g;
18+
X = x;
19+
Y = y;
20+
Rotation = rot;
21+
ScaleX = sx;
22+
ScaleY = sy;
23+
Z = z;
24+
}
25+
}

0 commit comments

Comments
 (0)