2011-10-19 4 views
0

J'écris une application qui permet à l'utilisateur de dessiner sur un écran tactile. J'utilise actuellement la méthode ci-dessous et cela fonctionne très bien. Cette méthode produit une "image à haute résolution" puisque pour presque chaque pixel une ligne est dessinée (par exemple 100, 100 -> 102, 103).Dessiner sur une toile un gros pixels

Voici ma question. Je voudrais que l'utilisateur dessine une «image basse résolution» (carte de grands pixels) où vous pouvez intentionnellement voir des pixels de 50 × 50 (par exemple 100, 100 -> 150, 150). Est-ce que quelqu'un a une idée sur la façon d'accomplir cela? J'utilise Silverlight pour Windows Phone. Je pensais à construire une grosse grille de 50 × 50 pixels, mais il pourrait y avoir trop de contrôles.

void FingerMove(object sender, MouseEventArgs e) 
{ 
    if (this.IsDrawing) 
    { 
     this.DestinationPoint = e.GetPosition(paint); 
     Line line = new Line 
     { 
      Stroke = this.Color, 
      X1 = this.DestinationPoint.X, 
      Y1 = this.DestinationPoint.Y, 
      X2 = this.OriginPoint.X, 
      Y2 = this.OriginPoint.Y, 
      StrokeStartLineCap = PenLineCap.Round, 
      StrokeEndLineCap = PenLineCap.Round, 
      StrokeThickness = 15, 
      Opacity = 1, 
     }; 

     Debug.WriteLine(string.Join(",", line.X1, line.Y1, line.X2, line.Y2)); 

     paint.Children.Add(line); 
    } 

    this.OriginPoint = this.DestinationPoint; 
} 
+0

avant de rendre à l'écran, dessiner un backbuffer (hors bitmap d'écran) échelle puis à l'autre avec les dimensions x et y 50 fois trop petit, haut de gamme pour finalement dessiner à l'écran , tous sans interpolation. –

+0

Voulez-vous dire que vous voulez voir une ligne bloquante, ou une ligne qui se conforme à une grille? –

+0

@GeorgeDuckett: Oui, c'est ce que je veux. – Martin

Répondre

1

@Amr a la bonne idée. Je vais vous donner ce code avec la mise en garde que je n'ai pas testé du tout. J'ai pris l'algorithme d'intersection de segments de ligne de here.

D'abord, vous devez établir une liste des Rectangles et les ajouter à la toile qui sont vos « pixels »:

private List<Rectangle> _rects = new List<Rectangle>(); 

    private void GenerateRects() 
    { 
     int width = 300; // or whatever dimensions... 
     int height = 300; 
     int gridSize = 50; 

     for (int x = 0; x < width; x += gridSize) 
     { 
      for (int y = 0; y < height; y += gridSize) 
      { 
       var rect = new Rectangle 
       { 
        Opacity = 0, 
        Width = Math.Min(gridSize, width - x), 
        Height = Math.Min(gridSize, height - y), 
       }; 

       Canvas.SetLeft(rect, x); 
       Canvas.SetTop(rect, y); 

       _rects.Add(rect); 
       this.paint.Children.Add(rect); 
      } 
     } 
    } 

Nous aurons besoin de ces méthodes d'assistance:

class LineSegment 
    { 
     public double X1 { get; set; } 
     public double X2 { get; set; } 
     public double Y1 { get; set; } 
     public double Y2 { get; set; } 
    } 

    private static bool SegmentsIntersect(LineSegment A, LineSegment B) 
    { 
     double x1 = A.X1, x2 = A.X2, x3 = B.X1, x4 = B.X2; 
     double y1 = A.Y1, y2 = A.Y2, y3 = B.Y1, y4 = B.Y2; 

     double denominator = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1); 

     if (denominator == 0) 
      return false; 

     double ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3))/denominator; 
     double ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3))/denominator; 

     return (ua > 0 && ua < 1 && ub > 0 && ub < 1); 
    } 

    private static bool RectIntersectsLine(Rect A, LineSegment B) 
    { 
     return (SegmentsIntersect(B, new LineSegment { X1 = A.X, Y1 = A.Y, X2 = A.X, Y2 = A.Y + A.Height }) || 
      SegmentsIntersect(B, new LineSegment { X1 = A.X, Y1 = A.Y + A.Height, X2 = A.X + A.Width, Y2 = A.Y + A.Height }) || 
      SegmentsIntersect(B, new LineSegment { X1 = A.X + A.Width, Y1 = A.Y + A.Height, X2 = A.X + A.Width, Y2 = A.Y }) || 
      SegmentsIntersect(B, new LineSegment { X1 = A.X + A.Width, Y1 = A.Y, X2 = A.X, Y2 = A.Y }) || 
      RectContainsPoint(A, new Point(B.X1, B.Y1)) || 
      RectContainsPoint(A, new Point(B.X2, B.Y2))); 
    } 

    private static bool RectContainsPoint(Rect A, Point B) 
    { 
     return (B.X > A.X && B.X < A.X + A.Width && B.Y > A.Y && B.Y < A.Y + A.Height); 
    } 

Ensuite, dans la fonction FingerMove, nous parcourons chaque Rectangle pour voir s'il s'intersecte. Si elle le fait, on change sa couleur:

void FingerMove(object sender, MouseEventArgs e) 
    { 
     if (this.IsDrawing) 
     { 
      this.DestinationPoint = e.GetPosition(paint); 
      LineSegment line = new LineSegment 
      { 
       X1 = this.DestinationPoint.X, 
       Y1 = this.DestinationPoint.Y, 
       X2 = this.OriginPoint.X, 
       Y2 = this.OriginPoint.Y 
      }; 

      foreach (var rect in _rects) 
      { 
       var x = Canvas.GetLeft(rect); 
       var y = Canvas.GetTop(rect); 

       if (RectIntersectsLine(new Rect(x, y, rect.Width, rect.Height) , line)) 
       { 
        rect.Opacity = 1; 
        rect.Fill = Color; 
       } 
      } 
     } 

     this.OriginPoint = this.DestinationPoint; 
    } 
0

Si vous voulez simplement rendre la ligne plus épaisse, juste expérimenter avec des valeurs possibles de StrokeThickness jusqu'à ce que vous obtenez l'effet désiré.

Si vous voulez dessiner manuellement la ligne en remplissant les grandes zones de l'écran disent rectangles (50x50), vous pouvez faire le follwing:

  1. diviser l'écran en rectangles de 50x50
  2. de contrôle qui rectangles sont coupées par la ligne tracée par l'utilisateur
  3. dessiner seulement les rectangles de l'étape 2

Cela vous donne la ligne « alignement sur la grille » que vous voulez.

Questions connexes