pre_int #4

Merged
aumuellerni87292 merged 4 commits from pre_int into main 2026-03-23 13:45:29 +00:00
12 changed files with 372 additions and 83 deletions
Showing only changes of commit 1baee83263 - Show all commits

View File

@ -1,12 +1,10 @@
package vassistent.ui;
import vassistent.bootstrap.ApplicationContext;
import vassistent.controller.DashboardController;
import vassistent.util.ConfigLoader;
import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;
import java.util.Locale;
import java.util.Properties;
/**
@ -14,6 +12,9 @@ import java.util.Properties;
*/
public class AppWindow extends JFrame {
private static final int EXPANDED_DASHBOARD_WIDTH = 460;
private static final int EXPANDED_DIVIDER_SIZE = 8;
private static final Properties config =
ConfigLoader.loadProperties(
"config/application.properties"
@ -22,6 +23,9 @@ public class AppWindow extends JFrame {
private PixelStreamingView streamingView;
private DashboardView dashboardView;
private JTabbedPane tabs;
private JSplitPane driveSplitPane;
private JPanel dashboardCard;
private JToggleButton dashboardToggleButton;
private JLabel mqttStatusLabel;
private JLabel problemLevelLabel;
@ -33,10 +37,12 @@ public class AppWindow extends JFrame {
setTitle("Virtueller Gesundheitsassistent");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(1400, 850);
setMinimumSize(new Dimension(1280, 720));
setSize(1700, 900);
setLocationRelativeTo(null);
getContentPane().setBackground(new Color(15, 19, 26));
setLayout(new BorderLayout(10,10));
setLayout(new BorderLayout(12, 12));
String streamUrl =
normalizeConfigValue(
@ -51,11 +57,13 @@ public class AppWindow extends JFrame {
dashboardView = new DashboardView();
add(createHeaderPanel(), BorderLayout.NORTH);
tabs = createTabPane();
add(tabs, BorderLayout.CENTER);
add(createStatusBar(), BorderLayout.SOUTH);
SwingUtilities.invokeLater(() -> setDashboardVisible(false));
Font uiFont = new Font("Segoe UI", Font.PLAIN, 14);
Font uiFont = new Font("Segoe UI", Font.PLAIN, 16);
UIManager.put("Label.font", uiFont);
UIManager.put("TabbedPane.font", uiFont);
}
@ -68,17 +76,96 @@ public class AppWindow extends JFrame {
private JTabbedPane createTabPane() {
JTabbedPane tabs = new JTabbedPane();
tabs.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
JPanel streamingPanel = new JPanel(new BorderLayout(10,10));
streamingPanel.add(streamingView, BorderLayout.CENTER);
tabs.addTab("Avatar Streaming", streamingView);
tabs.addTab("Dashboard", dashboardView);
tabs.setBorder(BorderFactory.createEmptyBorder(0, 10, 6, 10));
tabs.setFocusable(false);
tabs.putClientProperty("JTabbedPane.tabHeight", 38);
tabs.putClientProperty("JTabbedPane.hideTabAreaWithOneTab", true);
tabs.addTab("Drive View", createDriveViewPanel());
return tabs;
}
/**
* Creates the center-display optimized drive panel.
*
* @return drive panel with large assistant stream and compact dashboard
*/
private JPanel createDriveViewPanel() {
JPanel drivePanel = new JPanel(new BorderLayout());
drivePanel.setOpaque(false);
drivePanel.setBorder(BorderFactory.createEmptyBorder(6, 0, 0, 0));
JPanel streamCard = createCardPanel();
streamCard.setLayout(new BorderLayout(0, 12));
streamCard.add(
createSectionHeader(
"Virtual Health Assistant",
null
),
BorderLayout.NORTH
);
streamCard.add(streamingView, BorderLayout.CENTER);
dashboardCard = createCardPanel();
dashboardCard.setLayout(new BorderLayout(0, 12));
dashboardCard.add(
createSectionHeader(
"Health Overview",
"Live trend and current risk level"
),
BorderLayout.NORTH
);
dashboardCard.add(dashboardView, BorderLayout.CENTER);
dashboardCard.setPreferredSize(new Dimension(EXPANDED_DASHBOARD_WIDTH, 0));
dashboardCard.setMinimumSize(new Dimension(380, 0));
driveSplitPane =
new JSplitPane(
JSplitPane.HORIZONTAL_SPLIT,
streamCard,
dashboardCard
);
driveSplitPane.setBorder(null);
driveSplitPane.setOpaque(false);
driveSplitPane.setContinuousLayout(true);
driveSplitPane.setOneTouchExpandable(false);
driveSplitPane.setDividerSize(EXPANDED_DIVIDER_SIZE);
driveSplitPane.setResizeWeight(0.72);
driveSplitPane.setDividerLocation(0.72);
drivePanel.add(driveSplitPane, BorderLayout.CENTER);
return drivePanel;
}
/**
* Creates a top banner with project title and context.
*
* @return header panel
*/
private JPanel createHeaderPanel() {
JPanel header = new JPanel(new BorderLayout(12, 0));
header.setBorder(BorderFactory.createEmptyBorder(12, 16, 0, 16));
header.setOpaque(false);
JLabel title = new JLabel("Virtueller Gesundheitsassistent");
title.setFont(new Font("Segoe UI Semibold", Font.PLAIN, 30));
title.setForeground(new Color(238, 244, 255));
JPanel textStack = new JPanel();
textStack.setLayout(new BoxLayout(textStack, BoxLayout.Y_AXIS));
textStack.setOpaque(false);
title.setAlignmentX(Component.LEFT_ALIGNMENT);
textStack.add(title);
textStack.add(Box.createVerticalStrut(4));
header.add(textStack, BorderLayout.WEST);
header.add(createDashboardToggleButton(), BorderLayout.EAST);
return header;
}
/**
* Creates the status bar with MQTT and problem-level indicators.
*
@ -86,18 +173,18 @@ public class AppWindow extends JFrame {
*/
private JPanel createStatusBar() {
JPanel statusBar = new JPanel(new FlowLayout(FlowLayout.LEFT,15,5));
JPanel statusBar = new JPanel(new FlowLayout(FlowLayout.LEFT, 12, 8));
statusBar.setBackground(new Color(13, 16, 23));
statusBar.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createMatteBorder(1,0,0,0, Color.LIGHT_GRAY),
BorderFactory.createEmptyBorder(5,10,5,10)
BorderFactory.createMatteBorder(1, 0, 0, 0, new Color(54, 64, 78)),
BorderFactory.createEmptyBorder(6, 12, 8, 12)
));
mqttStatusLabel = new JLabel("MQTT: Disconnected");
problemLevelLabel = new JLabel("Problem: NONE");
mqttStatusLabel = createStatusChip("MQTT: Disconnected");
problemLevelLabel = createStatusChip("Problem: NONE");
statusBar.add(mqttStatusLabel);
statusBar.add(new JLabel(" | "));
statusBar.add(problemLevelLabel);
return statusBar;
@ -109,8 +196,11 @@ public class AppWindow extends JFrame {
* @param connected whether MQTT is currently connected
*/
public void updateMqttStatus(boolean connected) {
mqttStatusLabel.setText("MQTT: " +
(connected ? "Connected" : "Disconnected"));
mqttStatusLabel.setText("MQTT: "
+ (connected ? "Connected" : "Disconnected"));
mqttStatusLabel.setBackground(
connected ? new Color(30, 101, 70) : new Color(108, 47, 47)
);
}
/**
@ -120,6 +210,7 @@ public class AppWindow extends JFrame {
*/
public void updateProblemLevel(String level) {
problemLevelLabel.setText("Problem: " + level);
problemLevelLabel.setBackground(getProblemChipColor(level));
}
/**
@ -149,6 +240,160 @@ public class AppWindow extends JFrame {
return tabs;
}
/**
* Creates a panel with a subtle border and dark background.
*
* @return styled card panel
*/
private JPanel createCardPanel() {
JPanel card = new JPanel();
card.setOpaque(true);
card.setBackground(new Color(24, 31, 41));
card.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(new Color(61, 74, 92)),
BorderFactory.createEmptyBorder(12, 12, 12, 12)
));
return card;
}
/**
* Creates a section heading with title and subtitle text.
*
* @param title section title
* @param subtitle section subtitle
* @return heading panel
*/
private JPanel createSectionHeader(String title, String subtitle) {
JPanel panel = new JPanel();
panel.setOpaque(false);
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
JLabel titleLabel = new JLabel(title);
titleLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
titleLabel.setFont(new Font("Segoe UI Semibold", Font.PLAIN, 20));
titleLabel.setForeground(new Color(233, 241, 255));
JLabel subtitleLabel = new JLabel(subtitle);
subtitleLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
subtitleLabel.setFont(new Font("Segoe UI", Font.PLAIN, 13));
subtitleLabel.setForeground(new Color(143, 159, 182));
panel.add(titleLabel);
panel.add(Box.createVerticalStrut(2));
panel.add(subtitleLabel);
return panel;
}
/**
* Creates one status label used in the bottom status bar.
*
* @param text initial status text
* @return styled status label
*/
private JLabel createStatusChip(String text) {
JLabel label = new JLabel(text);
label.setOpaque(true);
label.setFont(new Font("Segoe UI Semibold", Font.PLAIN, 14));
label.setForeground(new Color(237, 244, 255));
label.setBackground(new Color(56, 66, 83));
label.setBorder(BorderFactory.createEmptyBorder(5, 12, 5, 12));
return label;
}
/**
* Returns a status chip background color for the current problem level.
*
* @param level problem level name
* @return matching background color
*/
private Color getProblemChipColor(String level) {
if (level == null) {
return new Color(56, 66, 83);
}
String normalized = level.trim().toUpperCase(Locale.ROOT);
switch (normalized) {
case "WARNING":
return new Color(134, 104, 29);
case "HIGH":
return new Color(168, 92, 31);
case "DISASTER":
return new Color(140, 49, 49);
default:
return new Color(46, 99, 61);
}
}
/**
* Creates the button used to toggle dashboard visibility.
*
* @return configured toggle button
*/
private JToggleButton createDashboardToggleButton() {
dashboardToggleButton = new JToggleButton("Dashboard einblenden");
dashboardToggleButton.setFocusable(false);
dashboardToggleButton.setFont(new Font("Segoe UI Semibold", Font.PLAIN, 14));
dashboardToggleButton.setForeground(new Color(233, 241, 255));
dashboardToggleButton.setBackground(new Color(39, 77, 57));
dashboardToggleButton.setBorder(BorderFactory.createEmptyBorder(8, 14, 8, 14));
dashboardToggleButton.addActionListener(
event -> setDashboardVisible(dashboardToggleButton.isSelected())
);
return dashboardToggleButton;
}
/**
* Shows or hides the dashboard side panel in drive view.
*
* @param visible whether dashboard should be visible
*/
private void setDashboardVisible(boolean visible) {
if (driveSplitPane == null || dashboardCard == null || dashboardToggleButton == null) {
return;
}
dashboardCard.setVisible(visible);
driveSplitPane.setDividerSize(visible ? EXPANDED_DIVIDER_SIZE : 0);
if (visible) {
dashboardToggleButton.setSelected(true);
dashboardToggleButton.setText("Dashboard ausblenden");
dashboardToggleButton.setBackground(new Color(69, 86, 109));
SwingUtilities.invokeLater(() -> {
int width = driveSplitPane.getWidth();
if (width <= 0) {
driveSplitPane.setDividerLocation(0.72);
return;
}
int dividerLocation = Math.max(
(int) (width * 0.55),
width - EXPANDED_DASHBOARD_WIDTH
);
driveSplitPane.setDividerLocation(dividerLocation);
});
} else {
dashboardToggleButton.setSelected(false);
dashboardToggleButton.setText("Dashboard einblenden");
dashboardToggleButton.setBackground(new Color(39, 77, 57));
SwingUtilities.invokeLater(() -> {
int width = driveSplitPane.getWidth();
if (width > 0) {
driveSplitPane.setDividerLocation(width);
} else {
driveSplitPane.setDividerLocation(1.0d);
}
});
}
driveSplitPane.revalidate();
driveSplitPane.repaint();
}
/**
* Trims a config value and strips optional wrapping quotes.
*

View File

@ -12,18 +12,15 @@ import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.ui.Layer;
import org.jfree.chart.ui.RectangleAnchor;
import org.jfree.chart.ui.TextAnchor;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.time.Millisecond;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import vassistent.model.ProblemLevel;
import vassistent.model.RatioPoint;
import javax.swing.*;
import java.awt.*;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
@ -42,28 +39,30 @@ public class DashboardView extends JPanel {
* Creates the dashboard panel with problem-level bar and time-series chart.
*/
public DashboardView() {
setLayout(new BorderLayout());
setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
setLayout(new BorderLayout(0, 12));
setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
setOpaque(false);
JPanel card = new JPanel(new BorderLayout(10,10));
card.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
card.setBackground(UIManager.getColor("Panel.background"));
JPanel card = new JPanel(new BorderLayout(0, 14));
card.setBackground(new Color(22, 28, 37));
card.setOpaque(true);
card.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(
UIManager.getColor("Component.borderColor")
new Color(58, 72, 90)
),
BorderFactory.createEmptyBorder(12,12,12,12)
BorderFactory.createEmptyBorder(14, 14, 14, 14)
));
// ---------- TOP: Problem Level ----------
JPanel topWrapper = new JPanel(new BorderLayout());
topWrapper.setBorder(BorderFactory.createEmptyBorder(0,0,10,0));
JPanel topWrapper = new JPanel(new BorderLayout(0, 8));
topWrapper.setBorder(BorderFactory.createEmptyBorder(0, 0, 8, 0));
topWrapper.setOpaque(false);
JLabel topLabel = new JLabel("Current Risk Level");
topLabel.setFont(new Font("Segoe UI Semibold", Font.PLAIN, 24));
topLabel.setForeground(new Color(230, 237, 249));
topWrapper.add(topLabel, BorderLayout.NORTH);
levelBar = new ProblemLevelBar();
topWrapper.add(levelBar, BorderLayout.CENTER);
@ -83,21 +82,28 @@ public class DashboardView extends JPanel {
);
XYPlot plot = chart.getXYPlot();
chart.setBackgroundPaint(new Color(22, 28, 37));
chart.getTitle().setPaint(new Color(222, 231, 245));
chart.getTitle().setFont(new Font("Segoe UI Semibold", Font.PLAIN, 16));
long tenMinutes = 10 * 60 * 1000L;
DateAxis domainAxis = (DateAxis) plot.getDomainAxis();
domainAxis.setFixedAutoRange(tenMinutes);
domainAxis.setAutoRange(true);
domainAxis.setDateFormatOverride(new SimpleDateFormat("HH:mm:ss"));
domainAxis.setLabelPaint(new Color(170, 185, 208));
domainAxis.setTickLabelPaint(new Color(170, 185, 208));
domainAxis.setTickMarkPaint(new Color(89, 105, 128));
XYLineAndShapeRenderer renderer =
(XYLineAndShapeRenderer) plot.getRenderer();
renderer.setDefaultShapesVisible(false);
renderer.setSeriesPaint(0, new Color(67, 175, 255));
renderer.setSeriesStroke(0,
new BasicStroke(
2.5f,
3.0f,
BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND
));
@ -110,14 +116,25 @@ public class DashboardView extends JPanel {
chart.getXYPlot().getRangeAxis().setRange(0.0, 1.02);
NumberAxis rangeAxis = (NumberAxis) chart.getXYPlot().getRangeAxis();
rangeAxis.setTickUnit(new NumberTickUnit(0.1));
rangeAxis.setLabelPaint(new Color(170, 185, 208));
rangeAxis.setTickLabelPaint(new Color(170, 185, 208));
rangeAxis.setTickMarkPaint(new Color(89, 105, 128));
plot.setBackgroundPaint(new Color(16, 20, 27));
plot.setOutlineVisible(false);
plot.setRangeGridlinePaint(new Color(89, 105, 128, 120));
plot.setDomainGridlinePaint(new Color(89, 105, 128, 120));
chart.setAntiAlias(true);
chart.setTextAntiAlias(true);
chart.getPlot().setBackgroundPaint(new Color(245,245,245));
chart.getPlot().setOutlineVisible(false);
ChartPanel chartPanel = new ChartPanel(chart);
chartPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
chartPanel.setOpaque(false);
chartPanel.setBackground(new Color(16, 20, 27));
chartPanel.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(new Color(58, 72, 90)),
BorderFactory.createEmptyBorder(8, 8, 8, 8)
));
card.add(chartPanel, BorderLayout.CENTER);
@ -188,7 +205,7 @@ public class DashboardView extends JPanel {
ValueMarker marker = new ValueMarker(value);
marker.setPaint(new Color(150,150,150,150));
marker.setPaint(getThresholdColor(label));
float[] dash = {6.0f, 6.0f};
marker.setStroke(new BasicStroke(
@ -201,12 +218,31 @@ public class DashboardView extends JPanel {
));
marker.setLabel(label);
marker.setLabelFont(new Font("Segoe UI", Font.PLAIN, 11));
marker.setLabelPaint(new Color(160,160,160));
marker.setLabelFont(new Font("Segoe UI Semibold", Font.PLAIN, 12));
marker.setLabelPaint(new Color(188, 203, 223));
marker.setLabelAnchor(RectangleAnchor.RIGHT);
marker.setLabelTextAnchor(TextAnchor.TOP_RIGHT);
plot.addRangeMarker(marker, Layer.BACKGROUND);
}
/**
* Returns a distinct marker color for each threshold level.
*
* @param label threshold label
* @return translucent marker color
*/
private Color getThresholdColor(String label) {
switch (label) {
case "Warning":
return new Color(255, 210, 69, 165);
case "High":
return new Color(255, 153, 65, 170);
case "Disaster":
return new Color(255, 92, 92, 175);
default:
return new Color(168, 178, 194, 140);
}
}
}

View File

@ -8,8 +8,6 @@ import org.cef.handler.CefAppHandlerAdapter;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* Embedded JCEF browser panel used to display the avatar pixel stream.
@ -31,7 +29,9 @@ public class PixelStreamingView extends JPanel {
*/
public PixelStreamingView(String startURL, boolean useOSR, boolean isTransparent) {
super(new BorderLayout());
setOpaque(true);
setBackground(new Color(8, 11, 16));
CefApp.addAppHandler(new CefAppHandlerAdapter(null) {
/**
* Handles JCEF application state transitions.
@ -55,7 +55,16 @@ public class PixelStreamingView extends JPanel {
browser = client.createBrowser(startURL, useOSR, isTransparent);
browserUI_ = browser.getUIComponent();
add(browserUI_, BorderLayout.CENTER);
JPanel browserContainer = new JPanel(new BorderLayout());
browserContainer.setOpaque(true);
browserContainer.setBackground(new Color(6, 9, 14));
browserContainer.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(new Color(56, 68, 86)),
BorderFactory.createEmptyBorder(6, 6, 6, 6)
));
browserContainer.add(browserUI_, BorderLayout.CENTER);
add(browserContainer, BorderLayout.CENTER);
}
/**

View File

@ -17,9 +17,10 @@ public class ProblemLevelBar extends JPanel {
* Creates the level bar component with preferred sizing and transparent background.
*/
public ProblemLevelBar() {
setPreferredSize(new Dimension(100, 50));
setPreferredSize(new Dimension(420, 88));
setMinimumSize(new Dimension(320, 72));
setOpaque(false);
setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
}
/**
@ -66,47 +67,45 @@ public class ProblemLevelBar extends JPanel {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
// --- Hintergrund ---
GradientPaint bg = new GradientPaint(
0, 0, new Color(235,235,235),
0, height, new Color(210,210,210)
int arc = Math.max(22, height / 2);
GradientPaint trackGradient = new GradientPaint(
0, 0, new Color(51, 60, 76),
0, height, new Color(29, 36, 48)
);
g2.setPaint(trackGradient);
g2.fillRoundRect(0, 0, width, height, arc, arc);
g2.setPaint(bg);
g2.fillRoundRect(0, 0, width, height, 20, 20);
// --- Gefüllter Bereich ---
int filledWidth = (int) (width * ratio);
if (filledWidth > 0) {
filledWidth = Math.max(filledWidth, arc / 2);
}
Color baseColor = getColorForLevel(level);
Color darkerColor = baseColor.darker();
GradientPaint gradient = new GradientPaint(
0, 0, darkerColor,
filledWidth, 0, baseColor
GradientPaint fillGradient = new GradientPaint(
0, 0, baseColor.darker(),
Math.max(1, filledWidth), 0, baseColor
);
g2.setPaint(gradient);
g2.fillRoundRect(0, 0, filledWidth, height, 20, 20);
g2.setPaint(fillGradient);
g2.fillRoundRect(0, 0, filledWidth, height, arc, arc);
g2.setStroke(new BasicStroke(2f));
g2.setColor(baseColor.brighter());
g2.drawRoundRect(1,1,width-2,height-2,20,20);
g2.setColor(new Color(175, 190, 212));
g2.drawRoundRect(1, 1, width - 2, height - 2, arc, arc);
// --- Text ---
String text = level.name() + " (" + (int)(ratio * 100) + "%)";
g2.setColor(Color.BLACK);
String text = "Problem " + level.name() + " " + (int) (ratio * 100) + "%";
g2.setFont(new Font("Segoe UI Semibold",
Font.PLAIN,
Math.max(19, (int) (height * 0.33))));
g2.setColor(new Color(244, 248, 255));
FontMetrics fm = g2.getFontMetrics();
int textWidth = fm.stringWidth(text);
g2.drawString(
text,
(width - textWidth) / 2,
(height + fm.getAscent()) / 2 - 3
);
int textX = (width - fm.stringWidth(text)) / 2;
int textY = (height - fm.getHeight()) / 2 + fm.getAscent();
g2.drawString(text, textX, textY);
}
/**
@ -118,13 +117,13 @@ public class ProblemLevelBar extends JPanel {
private Color getColorForLevel(ProblemLevel level) {
switch (level) {
case DISASTER:
return new Color(200, 0, 0);
return new Color(214, 70, 70);
case HIGH:
return new Color(255, 140, 0);
return new Color(234, 134, 45);
case WARNING:
return new Color(255, 215, 0);
return new Color(219, 180, 52);
default:
return new Color(0, 170, 0);
return new Color(53, 178, 107);
}
}
}

BIN
tmp_chunks/chunk_00.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
tmp_chunks/chunk_01.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
tmp_chunks/chunk_02.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
tmp_chunks/chunk_03.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
tmp_chunks/chunk_04.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
tmp_chunks/chunk_05.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
tmp_chunks/chunk_06.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
tmp_chunks/chunk_07.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB