Index: CHANGES
===================================================================
RCS file: /home/cvs/postgis/postgis/CHANGES,v
retrieving revision 1.192
diff -u -r1.192 CHANGES
--- CHANGES	22 Feb 2006 14:06:19 -0000	1.192
+++ CHANGES	23 Feb 2006 10:31:16 -0000
@@ -6,8 +6,9 @@
 	- Changed ./run_test script to have more sane default behaviour
 	- Fixed support for 64bit archs
 	- jdbc2 SRID handling fixes in JTS code
+	- New affine() matrix transformation functions
 	- New rotate{,X,Y,Z}() function 
-	- Obsoleted transscale() function
+	- Old translating and scaling functions now use affine() internally
 
 PostGIS 1.1.1
 2006/01/23
Index: doc/postgis.xml
===================================================================
RCS file: /home/cvs/postgis/postgis/doc/postgis.xml,v
retrieving revision 1.202
diff -u -r1.202 postgis.xml
--- doc/postgis.xml	22 Feb 2006 14:06:20 -0000	1.202
+++ doc/postgis.xml	23 Feb 2006 10:31:21 -0000
@@ -4709,6 +4709,64 @@
         </varlistentry>
 
         <varlistentry>
+          <term>Affine(geometry,float8,float8,float8,float8,float8,float8,float8,float8,float8,float8,float8,float8)</term>
+
+          <listitem>
+            <para>Applies an 3d affine transformation to the geometry. The call 
+                <programlisting>
+                    Affine(geom, a, b, c, d, e, f, g, h, i, xoff, yoff, zoff)
+                </programlisting>
+                represents the transformation matrix
+                <programlisting>
+                    /  a  b  c  xoff  \
+                    |  d  e  f  yoff  |
+                    |  g  h  i  zoff  |
+                    \  0  0  0  1     /
+                </programlisting>
+                and the vertices are transformed as follows:
+                <programlisting>
+                    x' = a*x + b*y + c*z + xoff
+                    y' = d*x + e*y + f*z + yoff
+                    z' = g*x + h*y + i*z + zoff   
+                </programlisting>
+                All of the translate / scale functions below are expressed via such an affine transformation.
+            </para>
+            <para>
+                Availability: 1.1.2.
+            </para>
+          </listitem>
+        </varlistentry>
+	
+        <varlistentry>
+          <term>Affine(geometry,float8,float8,float8,float8,float8,float8)</term>
+
+          <listitem>
+            <para>Applies an 2d affine transformation to the geometry. The call 
+                <programlisting>
+                    Affine(geom, a, b, d, e, xoff, yoff)
+                </programlisting>
+                represents the transformation matrix
+                <programlisting>
+                    /  a  b  0  xoff  \        /  a  b  xoff  \
+                    |  d  e  0  yoff  |  rsp.  |  d  e  yoff  |
+                    |  0  0  1  0     |        \  0  0  1     /
+                    \  0  0  0  1     /
+                </programlisting>
+                and the vertices are transformed as follows:
+                <programlisting>
+                    x' = a*x + b*y + xoff
+                    y' = d*x + e*y + yoff
+                    z' = z   
+                </programlisting>
+                This method is a subcase of the 3D method above.
+            </para>
+            <para>
+		Availability: 1.1.2.
+            </para>
+          </listitem>
+        </varlistentry>
+	
+        <varlistentry>
           <term>Translate(geometry,float8,float8,float8)</term>
 
           <listitem>
@@ -4750,12 +4808,13 @@
           <term>TransScale(geometry,float8,float8,float8,float8)</term>
 
           <listitem>
-            <para>First, translates the geometry using the first two floats, then scales it
-                using the second two floats, working in 2D only. Using
-                transscale(geom, X, Y, XFactor, YFactor) effectively is an efficient shortcut
-                for scale(translate(geom,X,Y,0),Xfactor,YFactor,1).</para>
+                <para>First, translates the geometry using the first two floats, then scales it
+                    using the second two floats, working in 2D only. Using
+                    <code>transscale(geom, X, Y, XFactor, YFactor)</code> internally calls
+                    <code>affine(geom, XFactor, 0, 0,  0, YFactor, 0,  0, 0, 1,  X*XFactor, Y*YFactor, 0)</code>.                
+                </para>
                 <para>
-                        Availability: 1.1.0.  DEPRECATED in release 1.1.2.
+                        Availability: 1.1.0.
                 </para>
           </listitem>
         </varlistentry>
Index: lwgeom/lwgeom_functions_basic.c
===================================================================
RCS file: /home/cvs/postgis/postgis/lwgeom/lwgeom_functions_basic.c,v
retrieving revision 1.138
diff -u -r1.138 lwgeom_functions_basic.c
--- lwgeom/lwgeom_functions_basic.c	22 Feb 2006 14:06:20 -0000	1.138
+++ lwgeom/lwgeom_functions_basic.c	23 Feb 2006 10:31:24 -0000
@@ -34,7 +34,6 @@
 Datum LWGEOM_perimeter_poly(PG_FUNCTION_ARGS);
 Datum LWGEOM_mindistance2d(PG_FUNCTION_ARGS);
 Datum LWGEOM_maxdistance2d_linestring(PG_FUNCTION_ARGS);
-Datum LWGEOM_transscale(PG_FUNCTION_ARGS);
 Datum LWGEOM_inside_circle_point(PG_FUNCTION_ARGS);
 Datum LWGEOM_collect(PG_FUNCTION_ARGS);
 Datum LWGEOM_accum(PG_FUNCTION_ARGS);
@@ -63,9 +62,6 @@
 Datum LWGEOM_azimuth(PG_FUNCTION_ARGS);
 Datum LWGEOM_affine(PG_FUNCTION_ARGS);
 
-static void lwgeom_transscale_recursive(uchar *serialized, double xoff, double yoff, double xfac, double yfac);
-static void lwgeom_transscale_ptarray(POINTARRAY *pa, double xoff, double yoff, double xfac, double yfac);
-
 Datum optimistic_overlap(PG_FUNCTION_ARGS);
 /*------------------------------------------------------------------*/
 
@@ -88,102 +84,6 @@
 	PG_RETURN_INT32(size);
 }
 
-
-/*
- * Transscale a pointarray.
- */
-static void
-lwgeom_transscale_ptarray(POINTARRAY *pa, double xoff, double yoff, double xfac, double yfac)
-{
-	int i;
-	POINT3DZ p3d;
-	POINT2D p2d;
-
-	if ( TYPE_HASZ(pa->dims) )
-	{
-		for (i=0; i<pa->npoints; i++) {
-			getPoint3dz_p(pa, i, &p3d);
-			p3d.x += xoff;
-			p3d.x *= xfac;
-			p3d.y += yoff;
-			p3d.y *= yfac;
-			/* Do nothing for z here */
-			memcpy(getPoint_internal(pa, i), &p3d,
-				sizeof(POINT3DZ));
-		}
-	}
-	else
-	{
-		for (i=0; i<pa->npoints; i++) {
-			getPoint2d_p(pa, i, &p2d);
-			p2d.x += xoff;
-			p2d.x *= xfac;
-			p2d.y += yoff;
-			p2d.y *= yfac;
-			memcpy(getPoint_internal(pa, i), &p2d,
-				sizeof(POINT2D));
-		}
-	}
-}
-
-static void
-lwgeom_transscale_recursive(uchar *serialized,
-	double xoff, double yoff, double xfac, double yfac)
-{
-	LWGEOM_INSPECTED *inspected;
-	int i, j;
-
-	inspected = lwgeom_inspect(serialized);
-
-	/* scan each object translating it */
-	for (i=0; i<inspected->ngeometries; i++)
-	{
-		LWLINE *line=NULL;
-		LWPOINT *point=NULL;
-		LWPOLY *poly=NULL;
-		uchar *subgeom=NULL;
-
-		point = lwgeom_getpoint_inspected(inspected, i);
-		if (point !=NULL)
-		{
-			lwgeom_transscale_ptarray(point->point,
-				xoff, yoff, xfac, yfac);
-			lwgeom_release((LWGEOM *)point);
-			continue;
-		}
-
-		poly = lwgeom_getpoly_inspected(inspected, i);
-		if (poly !=NULL)
-		{
-			for (j=0; j<poly->nrings; j++)
-			{
-				lwgeom_transscale_ptarray(poly->rings[j],
-					xoff, yoff, xfac, yfac);
-			}
-			lwgeom_release((LWGEOM *)poly);
-			continue;
-		}
-
-		line = lwgeom_getline_inspected(inspected, i);
-		if (line != NULL)
-		{
-			lwgeom_transscale_ptarray(line->points,
-				xoff, yoff, xfac, yfac);
-			lwgeom_release((LWGEOM *)line);
-			continue;
-		}
-
-		subgeom = lwgeom_getsubgeometry_inspected(inspected, i);
-		if ( subgeom == NULL )
-		{
-			elog(ERROR, "lwgeom_getsubgeometry_inspected returned NULL??");
-		}
-		lwgeom_transscale_recursive(subgeom, xoff, yoff, xfac, yfac);
-	}
-
-	pfree_inspected(inspected);
-}
-
 /* get summary info on a GEOMETRY */
 PG_FUNCTION_INFO_V1(LWGEOM_summary);
 Datum LWGEOM_summary(PG_FUNCTION_ARGS)
@@ -1670,33 +1570,6 @@
 	PG_RETURN_POINTER(ret);
 }
 
-/*transscale geometry */
-PG_FUNCTION_INFO_V1(LWGEOM_transscale);
-Datum LWGEOM_transscale(PG_FUNCTION_ARGS)
-{
-	PG_LWGEOM *geom = (PG_LWGEOM *)PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0));
-	PG_LWGEOM *ret;
-	LWGEOM *tmp;
-	uchar *srl = SERIALIZED_FORM(geom);
-
-	double xoff =  PG_GETARG_FLOAT8(1);
-	double yoff =  PG_GETARG_FLOAT8(2);
-	double xfac =  PG_GETARG_FLOAT8(3);
-	double yfac =  PG_GETARG_FLOAT8(4);
-
-	lwgeom_transscale_recursive(srl, xoff, yoff, xfac, yfac);
-
-	/* COMPUTE_BBOX TAINTING */
-	tmp = pglwgeom_deserialize(geom);
-	ret = pglwgeom_serialize(tmp);
-
-	/* Release memory */
-	pfree(geom);
-	lwgeom_release(tmp);
-
-	PG_RETURN_POINTER(ret);
-}
-
 PG_FUNCTION_INFO_V1(LWGEOM_inside_circle_point);
 Datum LWGEOM_inside_circle_point(PG_FUNCTION_ARGS)
 {
Index: lwgeom/lwpostgis.sql.in
===================================================================
RCS file: /home/cvs/postgis/postgis/lwgeom/lwpostgis.sql.in,v
retrieving revision 1.153
diff -u -r1.153 lwpostgis.sql.in
--- lwgeom/lwpostgis.sql.in	22 Feb 2006 14:06:20 -0000	1.153
+++ lwgeom/lwpostgis.sql.in	23 Feb 2006 10:31:27 -0000
@@ -221,11 +221,11 @@
 	AS 'SELECT scale($1, $2, $3, 1)'
 	LANGUAGE 'SQL' _IMMUTABLE_STRICT; 
 
--- Availability: 1.1.0 - DEPRECATED!
+-- Availability: 1.1.0 
 CREATEFUNCTION transscale(geometry,float8,float8,float8,float8)
         RETURNS geometry
-        AS '@MODULE_FILENAME@', 'LWGEOM_transscale'
-        LANGUAGE 'C' _IMMUTABLE_STRICT; -- WITH (isstrict) ;
+        AS 'SELECT affine($1,  $4, 0, 0,  0, $5, 0,  0, 0, 1,  $2*$4, $3*$5, 0)'
+        LANGUAGE 'SQL' _IMMUTABLE_STRICT;
 
 -- Availability: 1.1.0
 CREATEFUNCTION shift_longitude(geometry)
Index: regress/affine.sql
===================================================================
RCS file: /home/cvs/postgis/postgis/regress/affine.sql,v
retrieving revision 1.1
diff -u -r1.1 affine.sql
--- regress/affine.sql	22 Feb 2006 14:06:20 -0000	1.1
+++ regress/affine.sql	23 Feb 2006 10:31:27 -0000
@@ -27,3 +27,17 @@
 select 'rotateX', asewkt(SnapToGrid(rotateX('POINT(1 1 1)', pi()+pi()/2), 0.1));
 select 'rotateX', asewkt(SnapToGrid(rotateX('POINT(1 1 1)', 2*pi()), 0.1));
 
+-- transscale
+select 'transscale', asewkt(snapToGrid(transscale('POINT(1 1)',1, 1, 1, 1), 0.1));
+select 'transscale', asewkt(snapToGrid(transscale('POINT(2 2)',1, 1, 1, 1), 0.1));
+select 'transscale', asewkt(snapToGrid(transscale('POINT(1 1)',-1, -1, -1, -1), 0.1));
+select 'transscale', asewkt(snapToGrid(transscale('POINT(1 1)',0, 1, 1, 1), 0.1));
+select 'transscale', asewkt(snapToGrid(transscale('POINT(1 1)',1, 0, 1, 1), 0.1));
+select 'transscale', asewkt(snapToGrid(transscale('POINT(1 1)',1, 1, 0, 1), 0.1));
+select 'transscale', asewkt(snapToGrid(transscale('POINT(1 1)',1, 1, 1, 0), 0.1));
+select 'transscale', asewkt(snapToGrid(transscale('POINT(1 1)',2, 1, 1, 1), 0.1));
+select 'transscale', asewkt(snapToGrid(transscale('POINT(1 1)',1, 2, 1, 1), 0.1));
+select 'transscale', asewkt(snapToGrid(transscale('POINT(1 1)',1, 1, 2, 1), 0.1));
+select 'transscale', asewkt(snapToGrid(transscale('POINT(1 1)',1, 1, 1, 2), 0.1));
+select 'transscale', asewkt(snapToGrid(transscale('POINT(1 1)',2, 3, 5, 7), 0.1));
+select 'transscale', asewkt(snapToGrid(transscale('POINT(1 1 1)',2, 3, 5, 7), 0.1));
Index: regress/affine_expected
===================================================================
RCS file: /home/cvs/postgis/postgis/regress/affine_expected,v
retrieving revision 1.1
diff -u -r1.1 affine_expected
--- regress/affine_expected	22 Feb 2006 14:06:20 -0000	1.1
+++ regress/affine_expected	23 Feb 2006 10:31:27 -0000
@@ -15,3 +15,16 @@
 rotateX|POINT(1 -1 1)
 rotateX|POINT(1 1 -1)
 rotateX|POINT(1 1 1)
+transscale|POINT(2 2)
+transscale|POINT(3 3)
+transscale|POINT(0 0)
+transscale|POINT(1 2)
+transscale|POINT(2 1)
+transscale|POINT(0 2)
+transscale|POINT(2 0)
+transscale|POINT(3 2)
+transscale|POINT(2 3)
+transscale|POINT(4 2)
+transscale|POINT(2 4)
+transscale|POINT(15 28)
+transscale|POINT(15 28 1)
