[postgis-tickets] r16531 - #4063, optional false origin point for use in ST_Scale

Paul Ramsey pramsey at cleverelephant.ca
Fri Apr 6 11:52:17 PDT 2018


Author: pramsey
Date: 2018-04-06 11:52:17 -0700 (Fri, 06 Apr 2018)
New Revision: 16531

Modified:
   trunk/NEWS
   trunk/doc/reference_editor.xml
   trunk/liblwgeom/liblwgeom_internal.h
   trunk/liblwgeom/ptarray.c
   trunk/postgis/lwgeom_functions_basic.c
   trunk/postgis/postgis.sql.in
   trunk/regress/affine.sql
   trunk/regress/affine_expected
Log:
#4063, optional false origin point for use in ST_Scale


Modified: trunk/NEWS
===================================================================
--- trunk/NEWS	2018-04-06 10:28:00 UTC (rev 16530)
+++ trunk/NEWS	2018-04-06 18:52:17 UTC (rev 16531)
@@ -12,6 +12,7 @@
   - #2256, _postgis_index_extent() for extent from index (Paul Ramsey)
   - #3176, Add ST_OrientedEnvelope (Dan Baston)
   - #4029, Add ST_QuantizeCoordinates (Dan Baston)
+  - #4063, Optional false origin point for ST_Scale (Paul Ramsey)
 
 * Breaking Changes *
   - #4054, ST_SimplifyVW changed from > tolerance to >= tolerance

Modified: trunk/doc/reference_editor.xml
===================================================================
--- trunk/doc/reference_editor.xml	2018-04-06 10:28:00 UTC (rev 16530)
+++ trunk/doc/reference_editor.xml	2018-04-06 18:52:17 UTC (rev 16531)
@@ -1413,6 +1413,7 @@
 
 	  <refsynopsisdiv>
 		<funcsynopsis>
+
 		  <funcprototype>
 			<funcdef>geometry <function>ST_Scale</function></funcdef>
 			<paramdef><type>geometry </type> <parameter>geomA</parameter></paramdef>
@@ -1433,6 +1434,14 @@
 			<paramdef><type>geometry </type> <parameter>geom</parameter></paramdef>
 			<paramdef><type>geometry</type> <parameter>factor</parameter></paramdef>
 		  </funcprototype>
+
+		  <funcprototype>
+			<funcdef>geometry <function>ST_Scale</function></funcdef>
+			<paramdef><type>geometry </type> <parameter>geom</parameter></paramdef>
+			<paramdef><type>geometry</type> <parameter>factor</parameter></paramdef>
+			<paramdef><type>geometry</type> <parameter>origin</parameter></paramdef>
+		  </funcprototype>
+
 		</funcsynopsis>
 	  </refsynopsisdiv>
 
@@ -1449,6 +1458,9 @@
 supported dimensions. Missing dimensions in the <varname>factor</varname>
 point are equivalent to no scaling the corresponding dimension.
     </para>
+    <para>
+        The three-geometry variant allows a "false origin" for the scaling to be passed in. This allows "scaling in place", for example using the centroid of the geometry as the false origin. Without a false origin, scaling takes place relative to the actual origin, so all coordinates are just multipled by the scale factor.
+    </para>
 
 		<note><para>Prior to 1.3.4, this function crashes if used with geometries that contain CURVES.  This is fixed in 1.3.4+</para></note>
 
@@ -1486,6 +1498,11 @@
 ----------------------------------------
  LINESTRING(0.5 1.5 6 -4,0.5 0.75 2 -1)
 
+--Version 4: Scale X Y using false origin
+SELECT ST_AsText(ST_Scale('LINESTRING(1 1, 2 2)', 'POINT(2 2)', 'POINT(1 1)'::geometry));
+      st_astext
+---------------------
+ LINESTRING(1 1,3 3)
 
 </programlisting>
 	  </refsection>

Modified: trunk/liblwgeom/liblwgeom_internal.h
===================================================================
--- trunk/liblwgeom/liblwgeom_internal.h	2018-04-06 10:28:00 UTC (rev 16530)
+++ trunk/liblwgeom/liblwgeom_internal.h	2018-04-06 18:52:17 UTC (rev 16531)
@@ -307,6 +307,7 @@
 * Affine
 */
 void ptarray_affine(POINTARRAY *pa, const AFFINE *affine);
+void affine_invert(AFFINE *affine);
 
 /*
 * Scale

Modified: trunk/liblwgeom/ptarray.c
===================================================================
--- trunk/liblwgeom/ptarray.c	2018-04-06 10:28:00 UTC (rev 16530)
+++ trunk/liblwgeom/ptarray.c	2018-04-06 18:52:17 UTC (rev 16531)
@@ -1834,6 +1834,142 @@
 }
 
 /**
+* WARNING, make sure you send in only 16-member double arrays
+* or obviously things will go pear-shaped fast.
+*/
+#if 0
+static int gluInvertMatrix(const double *m, double *invOut)
+{
+    double inv[16], det;
+    int i;
+
+    inv[0] = m[5]  * m[10] * m[15] -
+             m[5]  * m[11] * m[14] -
+             m[9]  * m[6]  * m[15] +
+             m[9]  * m[7]  * m[14] +
+             m[13] * m[6]  * m[11] -
+             m[13] * m[7]  * m[10];
+
+    inv[4] = -m[4]  * m[10] * m[15] +
+              m[4]  * m[11] * m[14] +
+              m[8]  * m[6]  * m[15] -
+              m[8]  * m[7]  * m[14] -
+              m[12] * m[6]  * m[11] +
+              m[12] * m[7]  * m[10];
+
+    inv[8] = m[4]  * m[9] * m[15] -
+             m[4]  * m[11] * m[13] -
+             m[8]  * m[5] * m[15] +
+             m[8]  * m[7] * m[13] +
+             m[12] * m[5] * m[11] -
+             m[12] * m[7] * m[9];
+
+    inv[12] = -m[4]  * m[9] * m[14] +
+               m[4]  * m[10] * m[13] +
+               m[8]  * m[5] * m[14] -
+               m[8]  * m[6] * m[13] -
+               m[12] * m[5] * m[10] +
+               m[12] * m[6] * m[9];
+
+    inv[1] = -m[1]  * m[10] * m[15] +
+              m[1]  * m[11] * m[14] +
+              m[9]  * m[2] * m[15] -
+              m[9]  * m[3] * m[14] -
+              m[13] * m[2] * m[11] +
+              m[13] * m[3] * m[10];
+
+    inv[5] = m[0]  * m[10] * m[15] -
+             m[0]  * m[11] * m[14] -
+             m[8]  * m[2] * m[15] +
+             m[8]  * m[3] * m[14] +
+             m[12] * m[2] * m[11] -
+             m[12] * m[3] * m[10];
+
+    inv[9] = -m[0]  * m[9] * m[15] +
+              m[0]  * m[11] * m[13] +
+              m[8]  * m[1] * m[15] -
+              m[8]  * m[3] * m[13] -
+              m[12] * m[1] * m[11] +
+              m[12] * m[3] * m[9];
+
+    inv[13] = m[0]  * m[9] * m[14] -
+              m[0]  * m[10] * m[13] -
+              m[8]  * m[1] * m[14] +
+              m[8]  * m[2] * m[13] +
+              m[12] * m[1] * m[10] -
+              m[12] * m[2] * m[9];
+
+    inv[2] = m[1]  * m[6] * m[15] -
+             m[1]  * m[7] * m[14] -
+             m[5]  * m[2] * m[15] +
+             m[5]  * m[3] * m[14] +
+             m[13] * m[2] * m[7] -
+             m[13] * m[3] * m[6];
+
+    inv[6] = -m[0]  * m[6] * m[15] +
+              m[0]  * m[7] * m[14] +
+              m[4]  * m[2] * m[15] -
+              m[4]  * m[3] * m[14] -
+              m[12] * m[2] * m[7] +
+              m[12] * m[3] * m[6];
+
+    inv[10] = m[0]  * m[5] * m[15] -
+              m[0]  * m[7] * m[13] -
+              m[4]  * m[1] * m[15] +
+              m[4]  * m[3] * m[13] +
+              m[12] * m[1] * m[7] -
+              m[12] * m[3] * m[5];
+
+    inv[14] = -m[0]  * m[5] * m[14] +
+               m[0]  * m[6] * m[13] +
+               m[4]  * m[1] * m[14] -
+               m[4]  * m[2] * m[13] -
+               m[12] * m[1] * m[6] +
+               m[12] * m[2] * m[5];
+
+    inv[3] = -m[1] * m[6] * m[11] +
+              m[1] * m[7] * m[10] +
+              m[5] * m[2] * m[11] -
+              m[5] * m[3] * m[10] -
+              m[9] * m[2] * m[7] +
+              m[9] * m[3] * m[6];
+
+    inv[7] = m[0] * m[6] * m[11] -
+             m[0] * m[7] * m[10] -
+             m[4] * m[2] * m[11] +
+             m[4] * m[3] * m[10] +
+             m[8] * m[2] * m[7] -
+             m[8] * m[3] * m[6];
+
+    inv[11] = -m[0] * m[5] * m[11] +
+               m[0] * m[7] * m[9] +
+               m[4] * m[1] * m[11] -
+               m[4] * m[3] * m[9] -
+               m[8] * m[1] * m[7] +
+               m[8] * m[3] * m[5];
+
+    inv[15] = m[0] * m[5] * m[10] -
+              m[0] * m[6] * m[9] -
+              m[4] * m[1] * m[10] +
+              m[4] * m[2] * m[9] +
+              m[8] * m[1] * m[6] -
+              m[8] * m[2] * m[5];
+
+    det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12];
+
+    if (det == 0)
+        return LW_FALSE;
+
+    det = 1.0 / det;
+
+    for (i = 0; i < 16; i++)
+        invOut[i] = inv[i] * det;
+
+    return LW_TRUE;
+}
+#endif
+
+/**
  * Scale a pointarray.
  */
 void

Modified: trunk/postgis/lwgeom_functions_basic.c
===================================================================
--- trunk/postgis/lwgeom_functions_basic.c	2018-04-06 10:28:00 UTC (rev 16530)
+++ trunk/postgis/lwgeom_functions_basic.c	2018-04-06 18:52:17 UTC (rev 16531)
@@ -2919,48 +2919,95 @@
 PG_FUNCTION_INFO_V1(ST_Scale);
 Datum ST_Scale(PG_FUNCTION_ARGS)
 {
-  GSERIALIZED *geom1 = PG_GETARG_GSERIALIZED_P_COPY(0); /* will be modified */
-  GSERIALIZED *geom2 = PG_GETARG_GSERIALIZED_P(1);
-  GSERIALIZED *ret;
-  LWGEOM *lwgeom1 = lwgeom_from_gserialized(geom1);
-  LWGEOM *lwgeom2 = lwgeom_from_gserialized(geom2);
-  LWPOINT *lwpoint;
-  POINT4D factors;
+	GSERIALIZED *geom;
+	GSERIALIZED *geom_scale = PG_GETARG_GSERIALIZED_P(1);
+	GSERIALIZED *geom_origin = NULL;
+	LWGEOM *lwg, *lwg_scale, *lwg_origin;
+	LWPOINT *lwpt_scale, *lwpt_origin;
+	POINT4D origin;
+	POINT4D factors;
+	bool translate = false;
+	GSERIALIZED *ret;
+	AFFINE aff;
 
-  lwpoint = lwgeom_as_lwpoint(lwgeom2);
-  if ( lwpoint == NULL )
-  {
-    lwgeom_free(lwgeom1);
-    lwgeom_free(lwgeom2);
-    PG_FREE_IF_COPY(geom1, 0);
-    PG_FREE_IF_COPY(geom2, 1);
-    lwpgerror("Scale factor geometry parameter must be a point");
-    PG_RETURN_NULL();
-  }
-  if ( ! lwpoint->point->npoints )
-  {
-    /* empty point, return input untouched */
-    lwgeom_free(lwgeom1);
-    lwgeom_free(lwgeom2);
-    PG_FREE_IF_COPY(geom2, 1);
-    PG_RETURN_POINTER(geom1);
-  }
-  getPoint4d_p(lwpoint->point, 0, &factors);
-  if ( ! FLAGS_GET_Z(lwpoint->flags ) ) factors.z = 1;
-  if ( ! FLAGS_GET_M(lwpoint->flags ) ) factors.m = 1;
+	/* Make sure we have a valid scale input */
+	lwg_scale = lwgeom_from_gserialized(geom_scale);
+	lwpt_scale = lwgeom_as_lwpoint(lwg_scale);
+	if (!lwpt_scale)
+	{
+		lwgeom_free(lwg_scale);
+		PG_FREE_IF_COPY(geom_scale, 1);
+		lwpgerror("Scale factor geometry parameter must be a point");
+		PG_RETURN_NULL();
+	}
 
-  lwgeom_scale(lwgeom1, &factors);
+	/* Geom Will be modified in place, so take a copy */
+	geom = PG_GETARG_GSERIALIZED_P_COPY(0);
+	lwg = lwgeom_from_gserialized(geom);
 
-  /* Construct GSERIALIZED */
-  ret = geometry_serialize(lwgeom1);
+	/* Empty point, return input untouched */
+	if (lwgeom_is_empty(lwg))
+	{
+		lwgeom_free(lwg_scale);
+		lwgeom_free(lwg);
+		PG_FREE_IF_COPY(geom_scale, 1);
+		PG_RETURN_POINTER(geom);
+	}
 
-  /* Cleanup */
-  lwgeom_free(lwgeom1);
-  lwgeom_free(lwgeom2);
-  PG_FREE_IF_COPY(geom1, 0);
-  PG_FREE_IF_COPY(geom2, 1);
+	/* Once we read the scale data into local static point, we can */
+	/* free the lwgeom */
+	lwpoint_getPoint4d_p(lwpt_scale, &factors);
+	if (!lwgeom_has_z(lwg_scale)) factors.z = 1.0;
+	if (!lwgeom_has_m(lwg_scale)) factors.m = 1.0;
+	lwgeom_free(lwg_scale);
 
-  PG_RETURN_POINTER(ret);
+	/* Do we have the optional false origin? */
+	if (PG_NARGS() > 2 && !PG_ARGISNULL(2))
+	{
+		geom_origin = PG_GETARG_GSERIALIZED_P(2);
+		lwg_origin = lwgeom_from_gserialized(geom_origin);
+		lwpt_origin = lwgeom_as_lwpoint(lwg_origin);
+		if (lwpt_origin)
+		{
+			lwpoint_getPoint4d_p(lwpt_origin, &origin);
+			translate = true;
+		}
+		/* Free the false origin inputs */
+		lwgeom_free(lwg_origin);
+		PG_FREE_IF_COPY(geom_origin, 2);
+	}
+
+	/* If we have false origin, translate to it before scaling */
+	if (translate)
+	{
+		/* Initialize affine */
+		memset(&aff, 0, sizeof(AFFINE));
+		/* Set rotation/scale/sheer matrix to no-op */
+		aff.afac = aff.efac = aff.ifac = 1.0;
+		/* Strip false origin from all coordinates */
+		aff.xoff = -1 * origin.x;
+		aff.yoff = -1 * origin.y;
+		aff.zoff = -1 * origin.z;
+		lwgeom_affine(lwg, &aff);
+	}
+
+	lwgeom_scale(lwg, &factors);
+
+	/* Return to original origin after scaling */
+	if (translate)
+	{
+		aff.xoff *= -1;
+		aff.yoff *= -1;
+		aff.zoff *= -1;
+		lwgeom_affine(lwg, &aff);
+	}
+
+	/* Cleanup and return */
+	ret = geometry_serialize(lwg);
+	lwgeom_free(lwg);
+	PG_FREE_IF_COPY(geom, 0);
+	PG_FREE_IF_COPY(geom_scale, 1);
+	PG_RETURN_POINTER(ret);
 }
 
 Datum ST_Points(PG_FUNCTION_ARGS);

Modified: trunk/postgis/postgis.sql.in
===================================================================
--- trunk/postgis/postgis.sql.in	2018-04-06 10:28:00 UTC (rev 16530)
+++ trunk/postgis/postgis.sql.in	2018-04-06 18:52:17 UTC (rev 16531)
@@ -1856,6 +1856,12 @@
 	AS 'MODULE_PATHNAME', 'ST_Scale'
 	LANGUAGE 'c' IMMUTABLE STRICT _PARALLEL;
 
+-- Availability: 2.5.0
+CREATE OR REPLACE FUNCTION ST_Scale(geometry,geometry,origin geometry)
+	RETURNS geometry
+	AS 'MODULE_PATHNAME', 'ST_Scale'
+	LANGUAGE 'c' IMMUTABLE STRICT _PARALLEL;
+
 -- Availability: 1.2.2
 CREATE OR REPLACE FUNCTION ST_Scale(geometry,float8,float8,float8)
 	RETURNS geometry
@@ -3144,7 +3150,7 @@
 	AS 'MODULE_PATHNAME', 'LWGEOM_SetEffectiveArea'
 	LANGUAGE 'c' IMMUTABLE STRICT _PARALLEL
 	COST 1; -- reset cost, see #3675
-	
+
 -- Availability: 2.5.0
 CREATE OR REPLACE FUNCTION ST_FilterByM(geometry, double precision, double precision default null, boolean default false)
 	RETURNS geometry
@@ -3151,7 +3157,7 @@
 	AS 'MODULE_PATHNAME', 'LWGEOM_FilterByM'
 	LANGUAGE 'c' IMMUTABLE _PARALLEL
 	COST 1; -- reset cost, see #3675
-	
+
 -- Availability: 2.5.0
 CREATE OR REPLACE FUNCTION ST_ChaikinSmoothing(geometry, integer default 1, boolean default true)
 	RETURNS geometry
@@ -3158,7 +3164,7 @@
 	AS 'MODULE_PATHNAME', 'LWGEOM_ChaikinSmoothing'
 	LANGUAGE 'c' IMMUTABLE STRICT _PARALLEL
 	COST 1; -- reset cost, see #3675
-	
+
 -- ST_SnapToGrid(input, xoff, yoff, xsize, ysize)
 -- Availability: 1.2.2
 CREATE OR REPLACE FUNCTION ST_SnapToGrid(geometry, float8, float8, float8, float8)

Modified: trunk/regress/affine.sql
===================================================================
--- trunk/regress/affine.sql	2018-04-06 10:28:00 UTC (rev 16530)
+++ trunk/regress/affine.sql	2018-04-06 18:52:17 UTC (rev 16531)
@@ -13,6 +13,7 @@
 select 'ST_ScaleM1', ST_asewkt(ST_Scale('POINT(10 20 -5 3)'::geometry, ST_MakePoint(4, 2, -8)));
 select 'ST_ScaleM2', ST_asewkt(ST_Scale('POINT(-2 -1 3 2)'::geometry, ST_MakePointM(-2, 3, 4)));
 select 'ST_ScaleM3', ST_asewkt(ST_Scale('POINT(10 20 -5 3)'::geometry, ST_MakePoint(-3, 2, -1, 3)));
+select 'ST_ScaleOrigin', st_astext(st_scale('LINESTRING(1 1, 2 2)'::geometry, 'POINT(2 2)'::geometry, 'POINT(1 1)'::geometry));
 
 -- ST_Rotate
 select 'ST_Rotate', ST_asewkt(ST_SnapToGrid(ST_Rotate('POINT(1 1)'::geometry, pi()/2, 10.0, 20.0), 0.1));

Modified: trunk/regress/affine_expected
===================================================================
--- trunk/regress/affine_expected	2018-04-06 10:28:00 UTC (rev 16530)
+++ trunk/regress/affine_expected	2018-04-06 18:52:17 UTC (rev 16531)
@@ -7,6 +7,7 @@
 ST_ScaleM1|POINT(40 40 40 3)
 ST_ScaleM2|POINT(4 -3 3 8)
 ST_ScaleM3|POINT(-30 40 5 9)
+ST_ScaleOrigin|LINESTRING(1 1,3 3)
 ST_Rotate|POINT(29 11)
 ST_Rotate|POINT(-2 0)
 ST_Rotate|POINT(19 1)



More information about the postgis-tickets mailing list