Friday, April 19, 2013

Qt: Save QPainter Output in an SVG or Image File

To save what you paint in a Qt window to a file (SVG or image) is an easy and straightforward piece of code.

You create a QSvgGenerator or a QImage object.
Some initialization.
Paint into the object (treat it as an IODevice)
And  we are done.

Suppose that this is what I draw in my paint event:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void MainWindow::paintEvent(QPaintEvent *)
{
    QPainter painter;
    painter.begin(this);
     
    painter.setRenderHint(QPainter::Antialiasing);
    paint(painter);
     
    painter.end();
}
 
void MainWindow::paint(QPainter &painter)
{
    painter.setClipRect(QRect(0, 0, 200, 200));
    painter.setPen(Qt::NoPen);
    painter.fillRect(QRect(0, 0, 200, 200), Qt::gray);
    painter.setPen(QPen(Qt::white, 4, Qt::DashLine));
    painter.drawLine(QLine(0, 35, 200, 35));
    painter.drawLine(QLine(0, 165, 200, 165));
}


If I want to save it into an SVG file, it would be something like this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void MainWindow::saveSvg()
{
    QString path = QFileDialog::getSaveFileName(this, tr("Save as SVG"),"", tr("SVG file (*.svg)"));
 
    if (path.isEmpty())
        return;
 
    QSvgGenerator generator;
    generator.setFileName(path);
    generator.setSize(QSize(200, 200));
    generator.setViewBox(QRect(0, 0, 200, 200));
    generator.setTitle(tr("SVG Generator Example Drawing"));
    generator.setDescription(tr("An SVG drawing created by the SVG Generator "
                             "Example provided with Qt."));
    QPainter painter;
    painter.begin(&generator);
    paint(painter);
    painter.end();
}


If I want to save it into an image file, it would be something like this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void MainWindow::savePng()
{
    QString path = QFileDialog::getSaveFileName(this, tr("Save as image"), "", tr("PNG file (*.png)"));
 
    if (path.isEmpty())
        return;
 
    QImage img(200, 200, QImage::Format_ARGB32);
 
    QPainter painter;
    painter.begin(&img);
    paint(painter);
    painter.end();
 
    img.save(path);
}

Note that if you  replace
1
2
3
4
5
6
QImage img(200, 200, QImage::Format_ARGB32);
 
QPainter painter;
painter.begin(&img);
paint(painter);
painter.end();
with
1
2
3
QImage img(this->size(), QImage::Format_ARGB32);
QPainter painter(&img);
this->render(&painter);
you'll be taking a screenshot of widget content into an image.


References:
SVG Generator Example
Capture Qt widget as an image file

Thursday, April 11, 2013

Resizable Google Chart

To resize a Google chart (pie chart, area chart, geo chart ...) is a matter of a few lines of code after refactoring the given example in the Google Charts Gallery. The keyword is: window resize.

Just keep your data in a javascript variable and put the Google Chart code in a function to be called in each onResize event. That way the chart will be redraw to fit in the new size of the chart div, and it will look like it is resizing / scaling.

Note that you may want to set a min/max width/height for the size of the chart (css on the div can do the job just fine).

Performance? Just fine. Unless you have large data and many details to draw, then it may be a headache to draw from the beginning at each resize (you can limit the chart resize frequency with javascript by a delay or timer).

Here is a complete example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<html>
  <head>
    <script type="text/javascript" src="https://www.google.com/jsapi"></script>
    <script type="text/javascript">
    window.onresize = function(){
        startDrawingChart();
    };
 
    window.onload = function(){
        startDrawingChart();
    };
 
    var data_array = [
                      ['Year', 'Sales', 'Expenses'],
                      ['2004',  1000,      400],
                      ['2005',  1170,      460],
                      ['2006',  660,       1120],
                      ['2007',  1030,      540]
                    ];
                     
    startDrawingChart = function(){
        google.load("visualization", "1", {packages:["corechart"],callback: drawChart});
 
        function drawChart() {
            var data = google.visualization.arrayToDataTable(data_array);
 
            var options = {
              title: 'Company Performance',
              hAxis: {title: 'Year',  titleTextStyle: {color: 'red'}}
            };
 
            var chart = new google.visualization.AreaChart(document.getElementById('chart_div'));
            chart.draw(data, options);
        }
    };
    </script>
  </head>
  <body>
    <div id="chart_div"></div>
  </body>
</html>

Tuesday, April 9, 2013

Camera Drag and Zoom with Mouse in Unity 3D

The script is easy and descriptive, it is the result of some searching and modification.

  • Press the left mouse button and drag to move, and use mouse scroll wheel to zoom in and out.
  • The zooming code can work for both orthogonal and perspective but I've been using and testing it in orthogonal mode. 
  • Modify the public values to fit in your game.

** Just drag the script to your camera and it will work! **

C# code
[CameraDragZoom.cs]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
using UnityEngine;
using System.Collections;
 
public class CameraDragZoom : MonoBehaviour {
     
    public float dragSpeed = -10;
    public int minX = -892;
    public int maxX = 1111;
    public int minZ = -880;
    public int maxZ = 1145;
     
    public int bottomMargin = 80; // if you have some icons at the bottom (like an RPG game) this will help preventing the drag action at the bottom
     
    public float orthZoomStep = 10.0f;
    public int orthZoomMaxSize = 500;
    public int orthZoomMinSize = 300;
     
    private bool orthographicView = true;
    private Vector3 dragOrigin;
     
    // Update is called once per frame
    void Update () {
        moveCamera();
        zoomCamera();
    }
     
    void moveCamera()
    {
        if (Input.GetMouseButtonDown(0))
        {   
            dragOrigin = Input.mousePosition;
            return;
        }
 
        if (!Input.GetMouseButton(0)) return;
         
        if(dragOrigin.y <= bottomMargin) return;
         
        Vector3 pos = Camera.main.ScreenToViewportPoint(Input.mousePosition - dragOrigin);
        Vector3 move = new Vector3(pos.x * dragSpeed, 0, pos.y * dragSpeed);
                 
        if(move.x > 0)
        {
            if(!isWithinRightBorder())
                move.x =0;
        }
        else
        {
            if(!isWithinLeftBorder())
                move.x=0;
        }
         
        if(move.z > 0)
        {
            if(!isWithinTopBorder())
                move.z=0;
        }
        else
        {
            if(!isWithinBottomBorder())
                move.z=0;
        }
             
         
        transform.Translate(move, Space.World);
    }
     
    void zoomCamera()
    {
        if(!isWithinBorders())
            return;
         
        // zoom out
        if (Input.GetAxis("Mouse ScrollWheel") <0)
        {
            if(orthographicView)
            {
                if (Camera.main.orthographicSize <=orthZoomMaxSize)
                    Camera.main.orthographicSize += orthZoomStep;
            }
            else
            {
                if (Camera.main.fieldOfView<=150)
                       Camera.main.fieldOfView +=5;
            }
        }
        // zoom in
        if (Input.GetAxis("Mouse ScrollWheel") > 0)
           {
            if(orthographicView)
            {
                if (Camera.main.orthographicSize >= orthZoomMinSize)
                     Camera.main.orthographicSize -= orthZoomStep;           
            }
            else
            {
                if (Camera.main.fieldOfView>2)
                    Camera.main.fieldOfView -=5;
            }
           }
    }
     
    bool isWithinBorders()
    {
        return ( isWithinLeftBorder() && isWithinBottomBorder() && isWithinRightBorder() && isWithinTopBorder() );
    }
     
    bool isWithinLeftBorder()
    {
        Vector3 currentTopLeftGlobal = Camera.main.ScreenToWorldPoint(new Vector3(0,0,0));
        if(currentTopLeftGlobal.x > minX)
            return true;
        else
            return false;
         
    }
     
    bool isWithinRightBorder()
    {
        Vector3 currentBottomRightGlobal = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width,0,0));
        if(currentBottomRightGlobal.x < maxX)
            return true;
        else
            return false;
    }
     
    bool isWithinTopBorder()
    {
        Vector3 currentTopLeftGlobal = Camera.main.ScreenToWorldPoint(new Vector3(0,Screen.height,0));
        if(currentTopLeftGlobal.z < maxZ)
            return true;
        else
            return false;
    }
     
    bool isWithinBottomBorder()
    {
        Vector3 currentBottomRightGlobal = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width,0,0));
        if(currentBottomRightGlobal.z > minZ)
            return true;
        else
            return false;
    }
}