C# :: Aufgabe #190 :: Lösung #2

3 Lösungen Lösungen öffentlich
#190

Zählen von Flächensegmenten

Fortgeschrittener - C# von hollst - 09.10.2017 um 09:21 Uhr
1) Gegeben sei ein Quadrat mit der Seitenlänge L (z. B. L = 1).

2) Man wähle zufällig N Punktepaare auf den Seitenlinien des Quadrates, wobei nicht beide Punkte auf der gleichen Seitenlinie liegen dürfen.

3) Die Punkte eines Punktepaares werden nun jeweils durch eine Linie miteinander verbunden. Auf diese Weise wird die Fläche des Quadrates
in eine Vielzahl von Flächensegmenten unterteilt - siehe exemplarisch die zwei Abb. 1 und 2 mit N = 5 bzw. N = 6.

4) Man schreibe ein Programm, das die Anzahl der auf die Weise erzeugten Flächensegmente zurückgibt.
#2
vote_ok
von hollst (13980 Punkte) - 15.10.2017 um 18:21 Uhr
Quellcode ausblenden C#-Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

/*
        Install NuGet  
        Open your Visual Studio.  
        Open your solution/project.  
        Open Tools menu, select Library Package Manager and select Package Manager Console
        Run the following command "Install-Package Extended.Wpf.Toolkit" 
 */

namespace aufgabe_190
{
    public partial class MainWindow : Window
    {
        public String NL = Environment.NewLine, LZ = " ";

        public Boolean bo_init_ready = false, bo_is_loaded = false;
        public MainWindow()
        {
            InitializeComponent();
            column1.Width = new GridLength(0, GridUnitType.Star);
            update_gui();
            bo_init_ready = true;
            System.Windows.Forms.Application.DoEvents();
            this.bt_solve.IsEnabled = false;
            this.bt_refresh.IsEnabled = false;
        }

        Int32 N             = 10;
        Double factor       = 500;
        Int32 rect_Margin   = 150;
        Double lmin         = 1E-3;

        Double[][] abstandsmatrix;
        MyLine Quadrat;
        MyLine[] lines;
        List<Point> ListSchnittpunkte, ListUparteteSchnittpunkte, ListBorderpoints;

        private void b1_Click(object sender, RoutedEventArgs e)
        {
            do
            {
                System.Windows.Forms.Application.DoEvents();
                Thread.Sleep(1000);

                this.bt_solve.IsEnabled = true;
                this.bt_refresh.IsEnabled = true;
                this.b2_Click(null, null);
                this.tb2.Clear();
                update_gui();
                int loop_counter = 0;
                do
                {
                    loop_counter++;
                    Quadrat = new MyLine();
                    lines = Quadrat.RandomLines(N);
                    ListSchnittpunkte = Quadrat.ListSchnittpunkte(lines);
                    abstandsmatrix = Quadrat.Abstandsmatrix(ListSchnittpunkte, this.lmin);
                    ListUparteteSchnittpunkte = new List<Point>(Quadrat.upnormal_schnittpunkte);
                    ListBorderpoints = new List<Point>(Quadrat.borderpoints);
                    this.tb2.Text = loop_counter.ToString("n0");
                    System.Windows.Forms.Application.DoEvents();
                    if (loop_counter == 10000) break;
                }
                while ((abstandsmatrix.CountsLessThan(lmin) == 0) && (Boolean)this.cb1.IsChecked);

                this.tb1.Text =
                    "Linien: " + lines.Length.ToString() + NL + NL + lines.LineToString() + NL +
                    "Schnittpunkte: " + ListSchnittpunkte.Count.ToString() + NL + NL + ListSchnittpunkte.PointListToString() + NL +
                    "Abstandsmatrix: Anzahl Schnittpunktabstände kleiner als " + lmin.ToString() + ": " +
                        abstandsmatrix.CountsLessThan(lmin).ToString() +
                        NL + Quadrat.upnormal_schnittpunkte.PointListToString() +
                        NL + abstandsmatrix.DoubleMatrixToString();
                this.tb2.Text += LZ + (ListSchnittpunkte.Count + N + 1).ToString("X4") + LZ + ListUparteteSchnittpunkte.Count.ToString("X4");

                bt_refresh_Click(null, null);
            } while (false);
        }

        private void DrawRect()
        {
            Rectangle rect = new Rectangle            {
                Stroke = Brushes.Red,
                StrokeThickness = 1
            };

            rect.Margin = new Thickness(rect_Margin);
            rect.Width = factor;
            rect.Height = factor;
            SolidColorBrush mySolidColorBrush = new SolidColorBrush();
            mySolidColorBrush.Color = Brushes.Beige.Color;// Azure.Color;
            rect.Fill = mySolidColorBrush;
            this.canvas1.Children.Add(rect);
        }

        private void DrawText(Double[][] abstandsmatrix, MyLine[] lines, List<Point> ListSchnittpunkte)
        {
            TextBlock textBlock = new TextBlock();
            if (abstandsmatrix.CountsLessThan(lmin) == 0)
                textBlock.Text = (lines.Length + 1 + ListSchnittpunkte.Count).ToString("n0") + " area segments";
            else
                textBlock.Text = "degeneracy";

            textBlock.Foreground = new SolidColorBrush(Brushes.Blue.Color);
            textBlock.FontSize = 20;
            textBlock.FontWeight = FontWeights.UltraBold;
            textBlock.Margin = new Thickness(this.rect_Margin, this.factor + this.rect_Margin + textBlock.FontSize, 0, 0);

            this.canvas1.Children.Add(textBlock);
        }

        private void DrawLines(MyLine[] L, Double factor, int farbindex, Double linienstaerke, Int32 abstand00)
        {
            for (var i = 0; i < L.Length; i++)
            {
                Line myLine = new Line();
                myLine.Stroke = System.Windows.Media.Brushes.Black;
                if (farbindex == 1)
                    myLine.Stroke = System.Windows.Media.Brushes.Blue;
                if (farbindex == 2)
                    myLine.Stroke = System.Windows.Media.Brushes.Red;

                myLine.X1 = factor * L[i].p0.X + abstand00;
                myLine.X2 = factor * L[i].p1.X + abstand00;
                myLine.Y1 = factor * L[i].p0.Y + abstand00;
                myLine.Y2 = factor * L[i].p1.Y + abstand00;
                myLine.StrokeThickness = linienstaerke;
                this.canvas1.Children.Add(myLine);
            }
        }

        private void DrawCircles(List<Point> L, Brush farbindex, Double durchmesser, Double linienstaerke, Boolean bo_fill = false)
        {
            for (var i = 0; i < L.Count; i++)
            {
                Ellipse myEllipse = new Ellipse();
                //myEllipse.Stroke = System.Windows.Media.Brushes.Black;
                myEllipse.Stroke = farbindex;
                //if (farbindex == 1)
                //    myEllipse.Stroke = System.Windows.Media.Brushes.Blue;
                //if (farbindex == 2)
                //    myEllipse.Stroke = System.Windows.Media.Brushes.Red;
                myEllipse.StrokeThickness = linienstaerke;
                myEllipse.Width = durchmesser;
                myEllipse.Height = myEllipse.Width;
                if(bo_fill) myEllipse.Fill = farbindex;
                myEllipse.Margin = new Thickness(factor * L[i].X + rect_Margin - myEllipse.Width / 2, factor * L[i].Y + rect_Margin - myEllipse.Width / 2, 0, 0);
                this.canvas1.Children.Add(myEllipse);
            }
        }

        private void canvas1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            System.Windows.Point pos = e.GetPosition(this.canvas1);
            this.tb2.Text = ((pos.X - rect_Margin) / factor).ToString("0.0000").Digits(8) + " " + ((pos.Y - rect_Margin) / factor).ToString("0.0000").Digits(8);
            this.tb1.Text += NL + this.tb2.Text;
        }

        private void b2_Click(object sender, RoutedEventArgs e)
        {
            this.tb1.Clear(); this.tb2.Clear(); this.canvas1.Children.Clear();
            column1.Width = new GridLength(0, GridUnitType.Star);
        }

        private void myUpDownControl_ValueChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            this.N = (Int32)myUpDownControl.Value;
        }

        private void myUpDownControl1_ValueChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            this.factor = (Double)myUpDownControl1.Value;
        }

        private void myUpDownControl2_ValueChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            this.rect_Margin = (Int32)myUpDownControl2.Value;
        }

        private void bt_refresh_Click(object sender, RoutedEventArgs e)
        {
            if (!bt_refresh.IsEnabled)
                return;
            ListSchnittpunkte = Quadrat.ListSchnittpunkte(lines);
            abstandsmatrix = Quadrat.Abstandsmatrix(ListSchnittpunkte, this.lmin);
            ListUparteteSchnittpunkte = new List<Point>(Quadrat.upnormal_schnittpunkte);
            ListBorderpoints = new List<Point>(Quadrat.borderpoints);
            
            this.canvas1.Children.Clear();
            DrawRect();
            DrawLines(lines, this.factor, 2, 1, this.rect_Margin);
            if ((Boolean)this.cb3.IsChecked) DrawCircles(ListSchnittpunkte, System.Windows.Media.Brushes.Red, 10, 1);
            DrawCircles(ListUparteteSchnittpunkte, System.Windows.Media.Brushes.Red, 50, 5);
            if ((Boolean)this.cb4.IsChecked) DrawCircles(ListBorderpoints, System.Windows.Media.Brushes.Black, 10, 1, bo_fill: true);
            column1.Width = new GridLength(0, GridUnitType.Star);
        }

        private void cb_intersection_exact_Click(object sender, RoutedEventArgs e)
        {
            update_gui();
        }

        private void myUpDownControl3_ValueChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            if (bo_init_ready)
                update_gui(); 
        }

        private void b3_Click(object sender, RoutedEventArgs e)
        {
            DrawText(abstandsmatrix, lines, ListSchnittpunkte);
        }        

        private void update_gui()
        {
            if ((Boolean)cb_intersection_exact.IsChecked)
            {                
                this.myUpDownControl3.Visibility = Visibility.Hidden;
                this.label_myUpDownControl3.Visibility = Visibility.Hidden;
                this.lmin = 0;
            }
            else
            {
                this.myUpDownControl3.Visibility = Visibility.Visible;
                this.label_myUpDownControl3.Visibility = Visibility.Visible;
                this.lmin = 1.0E-6 * (Int32)myUpDownControl3.Value;
            }
            System.Windows.Forms.Application.DoEvents();
        }
    }

    public class Point
    {
        public Double X;
        public Double Y;

        public Point(Double x, Double y)
        {
            this.X = x;
            this.Y = y;
        }

        public Point() { }
    }

    public class MyLine
    {
        private Random rand = new Random();

        public Double a, b;
        public Point p0 = new Point(), p1 = new Point();
        public List<Point> schnittpunkte = new List<Point>();
        public List<Point> borderpoints = new List<Point>();
        public List<Point> upnormal_schnittpunkte = new List<Point>();
        public Double upnormal_abstand = 0.0;

        public MyLine() { }

        public MyLine(Point p0, Point p1)
        {
            double dx = p1.X - p0.X;
            double dy = p1.Y - p0.Y;
            if (dx == 0.0)
            {
                this.b = Double.PositiveInfinity;
                this.a = p1.X;
            }
            else
            {
                this.b = dy / dx;
                this.a = (p0.Y * p1.X - p1.Y * p0.X) / dx;
            }
        }

        public MyLine[] RandomLines(int N, Double h = 1.0)
        {
            borderpoints = new List<Point>();
            MyLine[] result = new MyLine[N];
            for (var i = 0; i < N; i++)
            {
                int kante1 = rand.Next(4);
                int kante2 = kante1;
                while (kante2 == kante1)
                    kante2 = rand.Next(4);
                double x1 = rand.NextDouble();
                double x2 = rand.NextDouble();
                Point p0 = new Point(), p1 = new Point();
                switch (kante1)
                {
                    case 0: p0 = new Point(x1, 0.0); break;
                    case 1: p0 = new Point(1.0, x1); break;
                    case 2: p0 = new Point(1.0 - x1, 1.0); break;
                    case 3: p0 = new Point(0.0, 1.0 - x1); break;
                }
                switch (kante2)
                {
                    case 0: p1 = new Point(x2, 0.0); break;
                    case 1: p1 = new Point(1.0, x2); break;
                    case 2: p1 = new Point(1.0 - x2, 1.0); break;
                    case 3: p1 = new Point(0.0, 1.0 - x2); break;
                }

                result[i] = new MyLine(p0, p1);
                borderpoints.Add(p0); borderpoints.Add(p1);
                result[i].p0 = p0;
                result[i].p1 = p1;
            }
            Point plo = new Point(0.0, 0.0);
            Point pro = new Point(0.0, 1.0);
            Point plu = new Point(1.0, 0.0);
            Point pru = new Point(1.0, 1.0);
            borderpoints.Add(plo); borderpoints.Add(pro); borderpoints.Add(plu); borderpoints.Add(pru);
            return result;
        }

        public Point Schnittpunkt(MyLine L1, MyLine L2)
        {
            Point result = new Point(Double.PositiveInfinity, Double.PositiveInfinity);

            if (L1.b == L2.b)
                return result;

            if ((L1.b == Double.PositiveInfinity) || (L2.b == Double.PositiveInfinity))
            {
                if (L1.b == Double.PositiveInfinity)
                {
                    result.X = L1.a;
                    result.Y = L2.a + L2.b * result.X;
                }
                else
                {
                    result.X = L2.a;
                    result.Y = L1.a + L1.b * result.X;
                }
                return result;
            }

            Double da = L2.a - L1.a, db = L2.b - L1.b;
            if (db == 0)
                return result;
            else
            {
                result.X = -da / db;
                result.Y = L1.a + L1.b * result.X;
            }
            return result;
        }

        public List<Point> ListSchnittpunkte(MyLine[] L, Double h = 1.0)
        {
            List<Point> result = new List<Point>();
            for(var i = 0; i < L.Length - 1; i++)
                for(var j = i + 1; j < L.Length; j++)
                {
                    Point s = Schnittpunkt(L[i], L[j]);
                    if ((s.X > 0.0) && (s.X < h) && (s.Y > 0.0) && (s.Y < h))
                        result.Add(s);
                }
            return result;
        }

        public Double[][] Abstandsmatrix(List<Point> L, Double upnormal_abstand)
        {
            this.upnormal_abstand = upnormal_abstand;
            upnormal_schnittpunkte = new List<Point>();

            Double[][] result = new Double[L.Count][];
            for(var i = 0; i < L.Count; i++)            {
                result[i] = new Double[L.Count];                
                for (var j = 0; j < result[i].Length; j++)
                {
                    Double dx = L[i].X - L[j].X, dy = L[i].Y - L[j].Y;
                    result[i][j] = Math.Sqrt(dx * dx + dy * dy);
                    if((i < j) && (result[i][j] < upnormal_abstand))
                        upnormal_schnittpunkte.Add(L[j]);
                }
            }
            return result;
        }
    }

    public static class MySpecialTools
    {
        public static string LZ = " ";

        public static string LineToString(this MyLine L)
        {
            StringBuilder sb = new StringBuilder();
            int nk = 8, digits = 11;
            string f = "0.";
            for (var i = 0; i < nk; i++)
                f += "0";
            sb.AppendLine(L.p0.X.ToString(f).Digits(digits) + LZ + L.p0.Y.ToString(f).Digits(digits) + LZ + L.p1.X.ToString(f).Digits(digits) + LZ + L.p1.Y.ToString(f).Digits(digits) +
                "    a: " + L.a.ToString(f).Digits(digits) + "  b: " + L.b.ToString(f).Digits(digits));
            return sb.ToString();
        }

        public static string LineToString(this MyLine[] L)
        {
            StringBuilder sb = new StringBuilder();
            for (var i = 0; i < L.Length; i++)
                sb.Append(L[i].LineToString());
            return sb.ToString();
        }

        public static string PointListToString(this List<Point> L)
        {
            StringBuilder sb = new StringBuilder();
            for (var i = 0; i < L.Count; i++)
                sb.AppendLine(L[i].X.ToString("0.0000").Digits(8) + LZ + L[i].Y.ToString("0.0000").Digits(8));
            return sb.ToString();
        }

        public static string Digits(this string s, int digits)
        {
            string result = s;
            while (result.Length < digits)
                result = " " + result;
            return result;
        }

        public static string DoubleMatrixToString(this Double[][] M, 
            String LK = "[", String RK = "]", String Trenner = " ", Int32 nachkomma = 2, Int32 digits = 8)
        {
            StringBuilder sb = new StringBuilder();
            String f = "0."; for (var i = 0; i < nachkomma; i++) f += "0";
            for(var i = 0; i < M.Length; i++)            {
                sb.Append(LK);
                for (var j = 0; j < M[i].Length; j++)
                    sb.Append(M[i][j].ToString(f).Digits(digits));
                sb.AppendLine(RK);
            }
            return sb.ToString();
        }

        public static int CountsLessThan(this Double[][] M, Double grenze)
        {
            int result = 0;
            for (var i = 0; i < M.Length; i++)
                for (var j = i + 1; j < M[i].Length; j++)
                    if (M[i][j] < grenze) result++;
            return result;
        }
    }
}

<Window x:Class="aufgabe_190.MainWindow"                        
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:landkartenproblem_wpf"
        xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
        mc:Ignorable="d"
        Title=" AUFGABE 190" Height="802.87" Width="1129.766" FontFamily="Courier New" WindowState="Maximized">
    <Grid>
        <TabControl x:Name="tabControl" Margin="0" TabStripPlacement="Bottom">
            <TabItem Header=" main ">
                <Grid Background="{DynamicResource {x:Static SystemColors.ActiveCaptionBrushKey}}">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="200"/>
                        <ColumnDefinition Width="100*" Name="column1"/>
                        <ColumnDefinition Width="100*"/>
                    </Grid.ColumnDefinitions>
                    <GridSplitter x:Name="gridSplitter" Grid.Column="2" Margin="0" Grid.RowSpan="2" Width="5" Background="{DynamicResource {x:Static SystemColors.ActiveCaptionBrushKey}}" ResizeDirection="Columns" HorizontalAlignment="Left"/>
                    <Canvas x:Name="canvas1" Grid.Column="2" Margin="20,15,15,15" Background="{DynamicResource {x:Static SystemColors.ActiveCaptionBrushKey}}" MouseLeftButtonDown="canvas1_MouseLeftButtonDown" Width="900" Height="900" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                    <StackPanel>
                        <GroupBox Header="commands" Margin="5" Background="{DynamicResource {x:Static SystemColors.ActiveCaptionBrushKey}}">
                            <StackPanel>
                                <Button x:Name="b2" Content="  clear  " Margin="5,15,5,15" Click="b2_Click" HorizontalAlignment="Center" FontWeight="Normal" FontSize="12"/>

                                <Button x:Name="b1" Content="  draw   " Margin="5" Click="b1_Click" HorizontalAlignment="Center" FontWeight="Normal" FontSize="12"/>

                                <Button x:Name="bt_solve" Content="  solve  " Margin="5" HorizontalAlignment="Center" Click="b3_Click" FontWeight="Normal" FontSize="12" />

                                <Button x:Name="bt_refresh"    Content=" refresh " Margin="5,15,5,15" HorizontalAlignment="Center" Click="bt_refresh_Click" FontWeight="Normal" FontSize="12"/>
                            </StackPanel>
                        </GroupBox>
                        <GroupBox Margin="5" Background="{DynamicResource {x:Static SystemColors.ActiveCaptionBrushKey}}" Header="settings" >
                            <StackPanel>
                                <Label Margin="5,5,5,0" Content="count lines (max 100)" FontSize="11" HorizontalAlignment="Left"/>
                                <xctk:IntegerUpDown Name="myUpDownControl" Maximum="100" Minimum="0" Value="10" Margin="50,0,50,5" ValueChanged="myUpDownControl_ValueChanged"/>

                                <Label Margin="5,5,5,0" Content="square border (pixel) " FontSize="11" HorizontalAlignment="Left"/>
                                <xctk:IntegerUpDown Name="myUpDownControl1" Minimum="50" Value="900" Margin="50,0,50,5" ValueChanged="myUpDownControl1_ValueChanged"/>

                                <Label Margin="5,5,5,0" Content="margin (pixel)" FontSize="11" HorizontalAlignment="Left"/>
                                <xctk:IntegerUpDown Name="myUpDownControl2" Minimum="0" Value="0" Margin="50,0,50,5" ValueChanged="myUpDownControl2_ValueChanged"/>

                                <Label Margin="5,5,5,0" Content="max inters. gap (1/10^6)" FontSize="11" HorizontalAlignment="Left" Name="label_myUpDownControl3"/>
                                <xctk:IntegerUpDown Name="myUpDownControl3" Minimum="0" Value="1000" Margin="50,0,50,15" ValueChanged="myUpDownControl3_ValueChanged" />

                            </StackPanel>
                        </GroupBox>

                        <GroupBox Header="options" Margin="5" Background="{DynamicResource {x:Static SystemColors.ActiveCaptionBrushKey}}">
                            <StackPanel>
                                <StackPanel Grid.Column="0" Grid.ColumnSpan="1" Orientation="Horizontal" Width="190">
                                    <Label Margin="5,5,5,0" Content="intersections exact" FontSize="11" VerticalAlignment="Center"/>
                                    <CheckBox x:Name="cb_intersection_exact"  HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10,0,0,0" Click="cb_intersection_exact_Click" />
                                </StackPanel>
                                <StackPanel Grid.Column="0" Grid.ColumnSpan="1" Orientation="Horizontal" Width="190">
                                    <Label Margin="5,5,5,0" Content="generate degeneracy" FontSize="11" VerticalAlignment="Center"/>
                                    <CheckBox x:Name="cb1"  HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10,0,0,0"/>
                                </StackPanel>
                                <StackPanel Grid.Column="0" Grid.ColumnSpan="1" Orientation="Horizontal" Width="190">
                                    <Label Margin="5,5,5,0" Content="show intersections " FontSize="11" VerticalAlignment="Center"/>
                                    <CheckBox x:Name="cb3"  HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10,0,0,0" Click="bt_refresh_Click"/>
                                </StackPanel>
                                <StackPanel Grid.Column="0" Grid.ColumnSpan="1" Orientation="Horizontal" Width="190">
                                    <Label Margin="5,5,5,0" Content="show border points " FontSize="11" VerticalAlignment="Center"/>
                                    <CheckBox x:Name="cb4"  HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10,0,0,0" Click="bt_refresh_Click"/>
                                </StackPanel>

                            </StackPanel>
                        </GroupBox>
                        <Label Margin="5,5,5,0"     Content=" intern infos " FontSize="11" />
                        <TextBox x:Name="tb2" Margin="10,5,18,5" TextWrapping="Wrap" AcceptsReturn="True" AcceptsTab="True" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"/>
                    </StackPanel>
                    <TextBox x:Name="tb1" Grid.Column="1" Margin="5" Text="TextBox" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" AcceptsReturn="True" AcceptsTab="True"/>
                </Grid>
            </TabItem>
            <TabItem Header=" reserve ">
                <Grid Background="#FFE5E5E5"/>

            </TabItem>
        </TabControl>

    </Grid>
</Window>

Kommentare:

Für diese Lösung gibt es noch keinen Kommentar

Bitte melden Sie sich an um eine Kommentar zu schreiben.
Kommentar schreiben