@@ -109,6 +109,109 @@ struct box_xyzm {
109109 }
110110};
111111
112+ struct affine_matrix {
113+
114+ // *------------*
115+ // | a b c xoff |
116+ // | d e f yoff |
117+ // | g h i zoff |
118+ // | 0 0 0 1 |
119+ // *------------*
120+ double v[16 ] = {};
121+
122+ static affine_matrix identity () {
123+ affine_matrix result;
124+ result.v [0 ] = 1 ;
125+ result.v [5 ] = 1 ;
126+ result.v [10 ] = 1 ;
127+ return result;
128+ }
129+
130+ static affine_matrix translate (double x, double y, double z = 0 ) {
131+ affine_matrix result = identity ();
132+ result.v [3 ] = x;
133+ result.v [7 ] = y;
134+ result.v [11 ] = z;
135+ return result;
136+ }
137+
138+ static affine_matrix scale (double x, double y, double z = 1 ) {
139+ affine_matrix result = identity ();
140+ result.v [0 ] = x;
141+ result.v [5 ] = y;
142+ result.v [10 ] = z;
143+ return result;
144+ }
145+
146+ static affine_matrix rotate_x (double angle) {
147+ affine_matrix result = identity ();
148+ const auto c = std::cos (angle);
149+ const auto s = std::sin (angle);
150+ result.v [5 ] = c;
151+ result.v [6 ] = -s;
152+ result.v [9 ] = s;
153+ result.v [10 ] = c;
154+ return result;
155+ }
156+
157+ static affine_matrix rotate_y (double angle) {
158+ affine_matrix result = identity ();
159+ const auto c = std::cos (angle);
160+ const auto s = std::sin (angle);
161+ result.v [0 ] = c;
162+ result.v [2 ] = s;
163+ result.v [8 ] = -s;
164+ result.v [10 ] = c;
165+ return result;
166+ }
167+
168+ static affine_matrix rotate_z (double angle) {
169+ affine_matrix result = identity ();
170+ const auto c = std::cos (angle);
171+ const auto s = std::sin (angle);
172+ result.v [0 ] = c;
173+ result.v [1 ] = -s;
174+ result.v [4 ] = s;
175+ result.v [5 ] = c;
176+ return result;
177+ }
178+
179+ static affine_matrix translate_scale (double x, double y, double z, double sx, double sy, double sz) {
180+ affine_matrix result = identity ();
181+ result.v [0 ] = sx;
182+ result.v [5 ] = sy;
183+ result.v [10 ] = sz;
184+ result.v [3 ] = x;
185+ result.v [7 ] = y;
186+ result.v [11 ] = z;
187+ return result;
188+ }
189+
190+ vertex_xy apply_xy (const vertex_xy &vertex) const {
191+
192+ // x = a * x + b * y + xoff;
193+ // y = d * x + e * y + yoff;
194+
195+ vertex_xy result = {0 , 0 };
196+ result.x = v[0 ] * vertex.x + v[1 ] * vertex.y + v[3 ];
197+ result.y = v[4 ] * vertex.x + v[5 ] * vertex.y + v[7 ];
198+ return result;
199+ }
200+
201+ vertex_xyzm apply_xyz (const vertex_xyzm &vertex) const {
202+
203+ // x = a * x + b * y + c * z + xoff;
204+ // y = d * x + e * y + f * z + yoff;
205+ // z = g * x + h * y + i * z + zoff;
206+
207+ vertex_xyzm result = {0 , 0 , 0 , 0 };
208+ result.x = v[0 ] * vertex.x + v[1 ] * vertex.y + v[2 ] * vertex.zm + v[3 ];
209+ result.y = v[4 ] * vertex.x + v[5 ] * vertex.y + v[6 ] * vertex.zm + v[7 ];
210+ result.zm = v[8 ] * vertex.x + v[9 ] * vertex.y + v[10 ] * vertex.zm + v[11 ];
211+ return result;
212+ }
213+ };
214+
112215enum class geometry_type : uint8_t {
113216 INVALID = 0 ,
114217 POINT,
@@ -812,6 +915,9 @@ inline double perimeter(const geometry *geom);
812915
813916namespace ops {
814917
918+ // If the allocator is null, transform in-place. Otherwise allocate new vertex data
919+ void affine_transform (sgl::allocator *alloc, sgl::geometry *geom, const sgl::affine_matrix *matrix);
920+
815921double area (const geometry *geom);
816922double perimeter (const geometry *geom);
817923double length (const geometry *geom);
@@ -820,6 +926,7 @@ int32_t max_surface_dimension(const geometry *geom, bool ignore_empty);
820926
821927double distance (const geometry* lhs, const geometry* rhs);
822928
929+ // This will NOT visit polygon rings if the requested dimension is 1
823930typedef void (*visit_func)(void *state, const geometry *part);
824931void visit_by_dimension (const geometry *geom, int surface_dimension, void *state, visit_func func);
825932
@@ -903,6 +1010,75 @@ bool is_valid(const sgl::geometry *geom);
9031010
9041011namespace sgl {
9051012namespace ops {
1013+
1014+ inline void affine_transform (sgl::allocator *alloc, sgl::geometry *geom, const sgl::affine_matrix *matrix) {
1015+ if (!geom) {
1016+ return ;
1017+ }
1018+
1019+ geometry *part = geom;
1020+ geometry *root = part->get_parent ();
1021+
1022+ while (true ) {
1023+ switch (part->get_type ()) {
1024+ case geometry_type::POINT:
1025+ case geometry_type::LINESTRING: {
1026+ const auto vertex_width = part->get_vertex_size ();
1027+ const auto vertex_count = part->get_count ();
1028+
1029+ if (vertex_count == 0 ) {
1030+ break ;
1031+ }
1032+
1033+ const auto old_vertex_data = part->get_vertex_data ();
1034+ auto new_vertex_data = old_vertex_data;
1035+ if (alloc) {
1036+ new_vertex_data = static_cast <uint8_t *>(alloc->alloc (vertex_width * vertex_count));
1037+ }
1038+
1039+ // Now, apply the transformation
1040+ vertex_xyzm old_vertex = {0 , 0 , 0 , 0 };
1041+ for (uint32_t i = 0 ; i < vertex_count; i++) {
1042+ memcpy (&old_vertex, old_vertex_data + i * vertex_width, vertex_width);
1043+ auto new_vertex = matrix->apply_xyz (old_vertex);
1044+ memcpy (new_vertex_data + i * vertex_width, &new_vertex, vertex_width);
1045+ }
1046+
1047+ part->set_vertex_data (new_vertex_data, vertex_count);
1048+ }
1049+ break ;
1050+ case geometry_type::POLYGON:
1051+ case geometry_type::MULTI_POINT:
1052+ case geometry_type::MULTI_LINESTRING:
1053+ case geometry_type::MULTI_POLYGON:
1054+ case geometry_type::MULTI_GEOMETRY:
1055+ if (!part->is_empty ()) {
1056+ part = part->get_first_part ();
1057+ continue ;
1058+ }
1059+ break ;
1060+ default :
1061+ SGL_ASSERT (false );
1062+ return ;
1063+ }
1064+
1065+ while (true ) {
1066+ const auto parent = part->get_parent ();
1067+ if (parent == root) {
1068+ return ;
1069+ }
1070+
1071+ if (part != parent->get_last_part ()) {
1072+ part = part->get_next ();
1073+ break ;
1074+ }
1075+
1076+ part = parent;
1077+ }
1078+ }
1079+ }
1080+
1081+
9061082inline double area (const geometry *geom) {
9071083 switch (geom->get_type ()) {
9081084 case geometry_type::POLYGON: {
@@ -1061,21 +1237,45 @@ inline void visit_by_dimension(const geometry *geom, int surface_dimension, void
10611237 while (true ) {
10621238 switch (part->get_type ()) {
10631239 case geometry_type::POINT:
1240+ if (surface_dimension == 0 ) {
1241+ func (state, part);
1242+ }
1243+ break ;
10641244 case geometry_type::MULTI_POINT:
10651245 if (surface_dimension == 0 ) {
10661246 func (state, part);
1247+ if (!part->is_empty ()) {
1248+ part = part->get_first_part ();
1249+ continue ;
1250+ }
10671251 }
10681252 break ;
10691253 case geometry_type::LINESTRING:
1254+ if (surface_dimension == 1 ) {
1255+ func (state, part);
1256+ }
1257+ break ;
10701258 case geometry_type::MULTI_LINESTRING:
10711259 if (surface_dimension == 1 ) {
10721260 func (state, part);
1261+ if (!part->is_empty ()) {
1262+ part = part->get_first_part ();
1263+ continue ;
1264+ }
10731265 }
10741266 break ;
10751267 case geometry_type::POLYGON:
1268+ if (surface_dimension == 2 ) {
1269+ func (state, part);
1270+ }
1271+ break ;
10761272 case geometry_type::MULTI_POLYGON:
10771273 if (surface_dimension == 2 ) {
10781274 func (state, part);
1275+ if (!part->is_empty ()) {
1276+ part = part->get_first_part ();
1277+ continue ;
1278+ }
10791279 }
10801280 break ;
10811281 case geometry_type::MULTI_GEOMETRY:
0 commit comments