1 /*
2 * Utilities for handling coordinate system transformations in filters
3 *
4 * Author:
5 * Niko Kiirala <niko@kiirala.com>
6 *
7 * Copyright (C) 2007 Niko Kiirala
8 *
9 * Released under GNU GPL, read the file 'COPYING' for more information
10 */
12 #include <glib.h>
14 #include "display/nr-filter-units.h"
15 #include "libnr/nr-rect-l.h"
16 #include "sp-filter-units.h"
17 #include <2geom/transforms.h>
19 using Geom::X;
20 using Geom::Y;
22 namespace Inkscape {
23 namespace Filters {
25 FilterUnits::FilterUnits() :
26 filterUnits(SP_FILTER_UNITS_OBJECTBOUNDINGBOX),
27 primitiveUnits(SP_FILTER_UNITS_USERSPACEONUSE),
28 resolution_x(-1), resolution_y(-1),
29 paraller_axis(false), automatic_resolution(true)
30 {}
32 FilterUnits::FilterUnits(SPFilterUnits const filterUnits, SPFilterUnits const primitiveUnits) :
33 filterUnits(filterUnits), primitiveUnits(primitiveUnits),
34 resolution_x(-1), resolution_y(-1),
35 paraller_axis(false), automatic_resolution(true)
36 {}
38 void FilterUnits::set_ctm(Geom::Matrix const &ctm) {
39 this->ctm = ctm;
40 }
42 void FilterUnits::set_resolution(double const x_res, double const y_res) {
43 g_assert(x_res > 0);
44 g_assert(y_res > 0);
46 resolution_x = x_res;
47 resolution_y = y_res;
48 }
50 void FilterUnits::set_item_bbox(Geom::OptRect const &bbox) {
51 item_bbox = bbox;
52 }
54 void FilterUnits::set_filter_area(Geom::OptRect const &area) {
55 filter_area = area;
56 }
58 void FilterUnits::set_paraller(bool const paraller) {
59 paraller_axis = paraller;
60 }
62 void FilterUnits::set_automatic_resolution(bool const automatic) {
63 automatic_resolution = automatic;
64 }
66 Geom::Matrix FilterUnits::get_matrix_user2pb() const {
67 g_assert(resolution_x > 0);
68 g_assert(resolution_y > 0);
69 g_assert(filter_area);
71 Geom::Matrix u2pb = ctm;
73 if (paraller_axis || !automatic_resolution) {
74 u2pb[0] = resolution_x / (filter_area->max()[X] - filter_area->min()[X]);
75 u2pb[1] = 0;
76 u2pb[2] = 0;
77 u2pb[3] = resolution_y / (filter_area->max()[Y] - filter_area->min()[Y]);
78 u2pb[4] = ctm[4];
79 u2pb[5] = ctm[5];
80 }
82 return u2pb;
83 }
85 Geom::Matrix FilterUnits::get_matrix_units2pb(SPFilterUnits units) const {
86 if ( item_bbox && (units == SP_FILTER_UNITS_OBJECTBOUNDINGBOX) ) {
87 Geom::Matrix u2pb = get_matrix_user2pb();
88 Geom::Point origo(item_bbox->min());
89 origo *= u2pb;
90 Geom::Point i_end(item_bbox->max()[X], item_bbox->min()[Y]);
91 i_end *= u2pb;
92 Geom::Point j_end(item_bbox->min()[X], item_bbox->max()[Y]);
93 j_end *= u2pb;
95 double len_i = sqrt((origo[X] - i_end[X]) * (origo[X] - i_end[X])
96 + (origo[Y] - i_end[Y]) * (origo[Y] - i_end[Y]));
97 double len_j = sqrt((origo[X] - j_end[X]) * (origo[X] - j_end[X])
98 + (origo[Y] - j_end[Y]) * (origo[Y] - j_end[Y]));
100 /* TODO: make sure that user coordinate system (0,0) is in correct
101 * place in pixblock coordinates */
102 Geom::Scale scaling(1.0 / len_i, 1.0 / len_j);
103 u2pb *= scaling;
104 return u2pb;
105 } else if (units == SP_FILTER_UNITS_USERSPACEONUSE) {
106 return get_matrix_user2pb();
107 } else {
108 g_warning("Error in Inkscape::Filters::FilterUnits::get_matrix_units2pb: unrecognized unit type (%d)", units);
109 return Geom::Matrix();
110 }
111 }
113 Geom::Matrix FilterUnits::get_matrix_filterunits2pb() const {
114 return get_matrix_units2pb(filterUnits);
115 }
117 Geom::Matrix FilterUnits::get_matrix_primitiveunits2pb() const {
118 return get_matrix_units2pb(primitiveUnits);
119 }
121 Geom::Matrix FilterUnits::get_matrix_display2pb() const {
122 Geom::Matrix d2pb = ctm.inverse();
123 d2pb *= get_matrix_user2pb();
124 return d2pb;
125 }
127 Geom::Matrix FilterUnits::get_matrix_pb2display() const {
128 Geom::Matrix pb2d = get_matrix_user2pb().inverse();
129 pb2d *= ctm;
130 return pb2d;
131 }
133 Geom::Matrix FilterUnits::get_matrix_user2units(SPFilterUnits units) const {
134 if (item_bbox && units == SP_FILTER_UNITS_OBJECTBOUNDINGBOX) {
135 /* No need to worry about rotations: bounding box coordinates
136 * always have base vectors paraller with userspace coordinates */
137 Geom::Point min(item_bbox->min());
138 Geom::Point max(item_bbox->max());
139 double scale_x = 1.0 / (max[X] - min[X]);
140 double scale_y = 1.0 / (max[Y] - min[Y]);
141 //return Geom::Translate(min) * Geom::Scale(scale_x,scale_y); ?
142 return Geom::Matrix(scale_x, 0,
143 0, scale_y,
144 min[X] * scale_x, min[Y] * scale_y);
145 } else if (units == SP_FILTER_UNITS_USERSPACEONUSE) {
146 return Geom::identity();
147 } else {
148 g_warning("Error in Inkscape::Filters::FilterUnits::get_matrix_user2units: unrecognized unit type (%d)", units);
149 return Geom::Matrix();
150 }
151 }
153 Geom::Matrix FilterUnits::get_matrix_user2filterunits() const {
154 return get_matrix_user2units(filterUnits);
155 }
157 Geom::Matrix FilterUnits::get_matrix_user2primitiveunits() const {
158 return get_matrix_user2units(primitiveUnits);
159 }
161 NR::IRect FilterUnits::get_pixblock_filterarea_paraller() const {
162 g_assert(filter_area);
164 int min_x = INT_MAX, min_y = INT_MAX, max_x = INT_MIN, max_y = INT_MIN;
165 Geom::Matrix u2pb = get_matrix_user2pb();
167 for (int i = 0 ; i < 4 ; i++) {
168 Geom::Point p = filter_area->corner(i);
169 p *= u2pb;
170 if (p[X] < min_x) min_x = (int)std::floor(p[X]);
171 if (p[X] > max_x) max_x = (int)std::ceil(p[X]);
172 if (p[Y] < min_y) min_y = (int)std::floor(p[Y]);
173 if (p[Y] > max_y) max_y = (int)std::ceil(p[Y]);
174 }
175 NR::IRect ret(NR::IPoint(min_x, min_y), NR::IPoint(max_x, max_y));
176 return ret;
177 }
179 FilterUnits& FilterUnits::operator=(FilterUnits const &other) {
180 filterUnits = other.filterUnits;
181 primitiveUnits = other.primitiveUnits;
182 resolution_x = other.resolution_x;
183 resolution_y = other.resolution_y;
184 paraller_axis = other.paraller_axis;
185 automatic_resolution = other.automatic_resolution;
186 ctm = other.ctm;
187 item_bbox = other.item_bbox;
188 filter_area = other.filter_area;
189 return *this;
190 }
192 } /* namespace Filters */
193 } /* namespace Inkscape */
196 /*
197 Local Variables:
198 mode:c++
199 c-file-style:"stroustrup"
200 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
201 indent-tabs-mode:nil
202 fill-column:99
203 End:
204 */
205 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :