[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