Vote Blue. Vote Democratic.

Bad men need nothing more to compass their ends, than that good men should look on and do nothing.

John Stuart Mill

At the time of writing, there are 70 days remaining until the next presidential election of the United States of America.

I look around and I see a government that is purposefully attempting to destroy its own postal service in what is obviously, by Donald Trump’s own statements, an attempt to reduce the ability for the citizens to vote. It is clearly a brazen attempt to sow chaos during the election in order to maximize the chances of remaining in power.

Although the center of this fascist hurricane is Donald Trump, as with the wreckage a hurricane leaves in its wake, the remnants of Republican actions to retain their power in the face of increasingly heinous acts by their elected officials are on display around the country and across the world. Starting with a refusal to demand more and better from one of their own, and allowing Donald Trump to drive a racist narrative, this has led to increased violence from white nationalist groups as well as given rise to the worst elements of disinformation and hate in our country. Donald Trump’s active cheering on of these groups, with not a word to countermand him from his Republican allies, has bolstered the attacks, verbal and physical, against those who stand against fascism and violence.

Beyond rhetoric, already a powerful weapon in the hands of a highly visible world leader, Donald Trump and those around him have personally profited, not just from their roles in government but specifically from the misery of others. From the wall, to the detention centers, to the FEMA contracts unfulfilled but fully paid, to the faulty PPE gear purchased from contractors with insider contacts… the list could go on for pages. Donald Trump has not only personally become wealthier by abusing the office of President, he has gone out of his way to hurt people in order to build up political power, stoke his base, and provide an “enemy” that allows him to be as aggressive and hostile as he wants, in order to channel the unrest of his base toward his own ends, by demonizing anyone who tries to stand in his way.

Finally, the Republican party in 2020 has chosen not to modify its platform, instead deciding that “support Trump” is the new platform. In history, this has typically been the rallying cry for fascist regimes and strongmen. Hitler notoriously altered the military swearing in to cite obedience to Hitler and not to the sovereign nation of Germany. Republicans, certainly any who still support Donald Trump, are complicit, willing partners to every denigration of this country at his hands.

I do not have much of an internet presence. I don’t even have Facebook and Twitter accounts. My website is not a high traffic stop. That has been a reason I have given myself for not posting anything like this; what is the point if no one reads it? But that excuse no longer holds water.

So… I’ll say it.

Vote Blue. Vote Democratic. Vote for freedom. Vote for free speech. Vote for peace. Vote for healing.

Vote against anyone who has tried to extend the Republican regime’s grip on the United States.

And more than that, be vocal.

Be heard. Be seen. Be known.

We are surrounded by fear. Fear can be used to control us. It can be easy if someone uses our fears to keep us from thinking for ourselves. When we are shouted at with nothing but terror, when we are shouted down when asking questions and told to stay in line, when our attempts at reason are met with appeals to emotion, when we are told that it is okay to hurt or demean a group of people solely because we fear them… when we succumb to fear, we are susceptible to hate. And hatred cannot be the foundation for justice and equality.

Ragdoll Mass Editor for Unity

I’ve recently run into a situation where I needed to alter the mass for each part of a humanoid ragdoll in my Unity game. I had blindly accepted the default mass of ’20’ in the ragdoll wizard and Unity happily apportioned that 20 mass units across the various parts of the ragdoll. I’m not sure what unit of measure we should pretend Unity uses that makes sense to apply 20 of to represent an adult humanoid, but here we are.

Anyway, I wrote the following script to create an editor that would allow you to choose an object representing the highest level ancestor of your ragdoll, then choose a new mass. It then collects the current mass total, creates a ratio, then applies that ratio to each of the child rigidbodies.

/*
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */
using UnityEngine;
using UnityEditor;
namespace Pyehouse.Editors
{
	public class RagdollMassAdjuster : EditorWindow
	{
		// Can be anything, make it smaller if you like...
		// just preventing divide by zero
		private const float SMALL_MASS_EPSILON = 0.00001f;
		private static bool TooSmall(float m)
		{
			return System.Math.Abs(m) < SMALL_MASS_EPSILON;
		}
		private static RagdollMassAdjuster editor;
		private static int width = 350;
		private static int height = 300;
		private static int x = 0;
		private static int y = 0;
		private bool noRigidbodies = false;
		private bool currentMassTooSmall = false;
		private float newMass = 20f;
		private GameObject ragdollParent;
		private void Awake()
		{
			ragdollParent = Selection.activeGameObject;
			Rigidbody[] rigidbodies = ragdollParent.GetComponentsInChildren<Rigidbody>();
			if (rigidbodies != null && rigidbodies.Length > 0)
			{
				float mass = 0f;
				foreach (Rigidbody rigidbody in rigidbodies)
				{
					mass += rigidbody.mass;
				}
				currentMassTooSmall = TooSmall(mass);
				newMass = mass;
				noRigidbodies = false;
			}
			else
			{
				newMass = 20f;
				noRigidbodies = true;
			}
		}
		[MenuItem("Window/Pyehouse/Ragdoll Mass Adjuster")]
		static void ShowEditor()
		{
			CenterWindow();
		}
		private static void CenterWindow()
		{
			editor = EditorWindow.GetWindow<RagdollMassAdjuster>();
			x = (Screen.currentResolution.width - width) / 2;
			y = (Screen.currentResolution.height - height) / 2;
			editor.position = new Rect(x, y, width, height);
			editor.maxSize = new Vector2(width, height);
			editor.minSize = editor.maxSize;
		}
		private void OnGUI()
		{
			if (currentMassTooSmall)
			{
				EditorGUILayout.HelpBox(string.Format("Current mass of selected ragdoll is too small: {0}", newMass), MessageType.Error);
			}
			else if (noRigidbodies)
			{
				EditorGUILayout.HelpBox("No rigidbodies present in selected GameObject or children.", MessageType.Error);
			}
			else
			{
				EditorStyles.label.wordWrap = true;
				EditorGUILayout.HelpBox("The new mass will be applied, maintaining the same per-object ratio as currently exists.", MessageType.Info);
				EditorGUILayout.HelpBox("So if your head is currently 3 out of a total 20 and you want a total mass of 100, your head will become 15.", MessageType.Info);
				newMass = EditorGUILayout.FloatField("New Mass for Ragdoll:", newMass);
				if (GUILayout.Button("Update Mass"))
				{
					UpdateMass(newMass);
				}
			}
		}
		private void UpdateMass(float toMass)
		{
			if (ragdollParent == null) return;
			Rigidbody[] rigidbodies = ragdollParent.GetComponentsInChildren<Rigidbody>();
			if (rigidbodies == null || rigidbodies.Length < 1)
			{
				Debug.LogErrorFormat("No rigidbodies found on selected object.");
				return;
			}
			float fromMass = 0f;
			foreach (Rigidbody rigidbody in rigidbodies)
			{
				fromMass += rigidbody.mass;
			}
			if (TooSmall(fromMass))
			{
				Debug.LogErrorFormat("Current mass too small: {0}", fromMass);
				return;
			}
			float ratio = toMass / fromMass;
			foreach (Rigidbody rigidbody in rigidbodies)
			{
				rigidbody.mass *= ratio;
				EditorUtility.SetDirty(rigidbody);
				EditorSceneManager.MarkSceneDirty(rigidbody.transform.gameObject.scene);
			}
		}
	}
}

cscreen source released under GPL v3

I’ve made the cscreen source available at github under GPL v3.

https://github.com/lynnpye/cscreen

You use at your own risk, as always, but if anyone would like to offer updates or would like to take it and run with it, the source is now freely available.

Sorry about the wait. 🙂

Weblogic, Kerberos, Single Sign On and You

We recently needed to implement single sign on in Weblogic for a client and finally managed to get it working, but had more difficulty than we probably ought to have. While some of our problems stemmed from our lack of experience with Weblogic, we also ran into problems with the documentation we were pointed to being vague, incomplete, and/or misleading. My goal with this post is to hopefully help others avoid some of the same misunderstandings and pitfalls we experienced.

First, I’ll point you to three pertinent links which helped. Understand, though, that there were a number of other things we ran across through various searches that led to us working these issues out. The first two are official Oracle documents and the third was a scribd article that helped us put some pieces together.

How To Configure Browser-based SSO with Kerberos/SPNEGO and Oracle WebLogic Server

Configuring Single Sign-On with Microsoft Clients

Enable Kerberos With Workspace 11.1.1.3

As I said, the first two links are to Oracle documentation. I believe the second link is a bit more useful, to be honest, though the first does have a little sample servlet code you can slap into place for testing; but keep in mind that the instructions to make it work are incomplete. The useful bits on both have more to do with configuring Kerberos (e.g. account and SPN generation, keytab generation and installation) but leave a little out that would be very useful. The third article is a scribd article that describes exactly what the title indicates, but the setup for Kerberos was helpful in terms of getting the keytab right.

The first problem we had was with the account and keytab generation. We were using the first Oracle link which was a little more spartan with its explanation of how to set up the accounts in Active Directory and set up the keytab file. Sure, it has images and explanations but the selection of account and machine names is ridiculously confusing. The second Oracle link has a better explanation of the account setup and keytab generation but still includes some information that is either unnecessary or misleading.

  1. In the Active Directory create a user account which will be doing ‘Kerberos things’. e.g. svc_someapp_krb
    1. By all means, ignore the suggestion of using the name of the client machine for the account name. Adhere to whatever naming convention makes the most sense. This one suggestion led to a lot of confusion while trying to decide how to configure the ‘ktpass’ command.
    2. ‘ktpass’ will create an SPN for you, but you can also do it yourself. The instructions suggest creating the SPN separately. It matters little.
    3. Keep in mind what encryption type to select in AD for the user. It must be consistent. We used AES 128. If you go higher than this remember you’ll need to download Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files here and install them properly.
    4. Make note of the fact that changing the encryption type AFTER setting your password can corrupt the encrypted password. Reset your password (even if to the current value) if you change encryption.
    5. Sometimes Java implementations for keytabs have problems with special characters. What is a special character? We wound up using strictly alphanumerics to avoid the problem. We couldn’t find a firm definition of what was allowed. You could try underscores and hyphens. Much more than that and you begin risking running into this issue.
  2. For our ‘ktpass’ our principal was of the form ‘HTTP/ourserver.some.domain.com@SOME.DOMAIN.COM’. Notice the caps? You’ll need that. Notice the repetition of the domain? You’ll need that to. So our ‘ktpass’ was like:
    1. ktpass /princ HTTP/weblogicserver.domain.com@DOMAIN.COM /pass domainuserpassword  /mapuser domainuser@domain.com /ptype KRB5_NT_PRINCIPAL /out weblogic.keytab /crypto AES128-SHA1 /kvno 0
  3. If you are not using node manager, but are running your Weblogic server as a Windows service, you presumably already set up your install.cmd file. You’ll need to add the specified options as JAVA_OPTIONS here. Remember, the Server->Server Start arguments are only useful when the Node Manager is handling things on a remote server. For local setup you’ll need to use different means. We ended up using full paths for our various files.
  4. If you don’t have access to the C:\Windows\ folder, you’ll need to create your krb5.ini file and place it somewhere else, then add a startup option to point to it as the default location can’t be used. You can do this via the following additional JAVA_OPTIONS parameter:
    1. -Djava.security.krb5.conf=C:\OurApp\krbstuff\krb5.ini

Keeping these points in mind while setting up things for Kerberos will help you avoid some of the issues we ran into. At this point we were able to add the NegotiateIdentityProvider to our security realm. The Kerberos debug settings allowed us to see that the ticket setup was working, plus kinit confirmed things too. But the test servlet still wasn’t working.

So what went wrong next? For starters, our inexperience with Weblogic led to a lack of understanding of how the authentication system works. Let’s take a look at that sample servlet in the first link. The weblogic.xml snippet (well, it’s complete but tiny for sample purposes) looks like this:

<!DOCTYPE weblogic-web-app PUBLIC "-//BEA Systems, Inc.//DTD Web Application 8.1//EN" "http://www.bea.com/servers/wls810/dtd/weblogic810-web-jar.dtd">
<weblogic-web-app>
  <security-role-assignment>
    <role-name>negotiateAdminRole</role-name>
    <principal-name>negotiateAdmin</principal-name>
    <principal-name>Administrators</principal-name>
  </security-role-assignment>
</weblogic-web-app>

If you understand this part, please skip it. This is just saying that “if something in the Weblogic security layer declares that the current visitor is a ‘negotiateAdmin’ thing or an ‘Administrators’ thing, then for this servlet we will treat it is a ‘negotiateAdminRole’ thing”. I’m dumbing it down a bit, but given our inexperience with Weblogic when we were setting things up, we didn’t yet know how Weblogic would determine that the current user was a ‘negotiateAdmin’ or an ‘Administrators’ member. We had the notion of an LDAP or AD (via LDAP) lookup but nothing had yet crystallized.

Now for a snippet from the sample web.xml:

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>BasicAuthSimpleTestServlet</web-resource-name>
            <url-pattern>/*</url-pattern>
            <url-pattern>/</url-pattern>
            <http-method>POST</http-method>
            <http-method>GET</http-method>
        </web-resource-collection>
        <auth-constraint>
            <role-name>negotiateAdminRole</role-name>
        </auth-constraint>
        <user-data-constraint>
            <description>no description</description>
            <transport-guarantee>NONE</transport-guarantee>
        </user-data-constraint>
    </security-constraint>
    <security-role>
        <role-name>negotiateAdminRole</role-name>
    </security-role>
    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>default</realm-name>
    </login-config>

Starting from the bottom and going up, check out the auth-method. BASIC is going to trigger a login prompt. We want single sign on and this page is meant to be an example of such using Kerberos. You want CLIENT-CERT here, not BASIC. CLIENT-CERT says “hey, browser, you are GOING to give me a certificate of some kind, including possibly a Kerberos ticket, or you will be denied”. Quite simple. I suppose the sample was intended to give a visual cue but typically a browser would be configured to do that anyway if an ‘Authorization: Negotiate’ header came back from the server so again I’m not sure why this change was left in. Anyhow…

Moving up, we declare the existence of a security-role called ‘negotiateAdminRole’. This makes it available for reference elsewhere, in this case in the auth-constraints tag agove it. If you take a look in the security-constraint you get the gist of what is going on; the servlet is being set up with its available access methods and is constrained to only those users with the negotiateAdminRole assigned. Now, recall the web.xml where we indicated who would be granted the negotiateAdminRole? That made some sense to us, but what was missing was the following:

The mapping of principal-name to role-name in web.xml is based on mapping whatever identity is retrieved from authentication providers to retrieved roles from various role providers via a simple string match.

Let’s jump ahead a bit. Because of the NegotiateIdentityProvider in our security realm, Weblogic knew what our account name was due to the Kerberos ticket authentication taking place. So user ‘jsmith’ comes along and now the current user is identified as user ‘jsmith’. But ‘jsmith’ doesn’t belong to any roles and gets no principals assigned. Weblogic will take the user account and check with other providers to see if they know who ‘jsmith’ is and see if it has any group/role memberships.

It turns out that to make the sample servlet work, you can simply add a user to the built in Weblogic Users/Groups with the same account name as an account from the Kerberos domain, in this case ‘jsmith’. Now add ‘jsmith’ to a Weblogic group as used in the sample servlet, e.g. ‘Administrators’. That will immediately make the sample servlet start working assuming you are ‘jsmith’. Moreover, it means that ‘jsmith’ will become an Administrator when hitting the Weblogic console so you now have single sign on working for your console.

But now the next part comes in, linking to your Active Directory implementation for LDAP lookups. Just as you added the NegotiateIdentityProvider, you can also add an ActiveDirectoryAuthentication provider as well as LDAPAuthentication. We read a number of articles suggesting that even if you are targeting Active Directory, you may still want to use LDAPAuthenticator to work around some problems. We in fact tried LDAP first but ended up getting ActiveDirectoryAuthenticator to work for us. The trick boils down to what you use for the Principal name and the various filters. You need an account you can log in with, preferably a service account of some kind. Some suggested you could log in with the Principal set to just the account name. We ended up having to use the full distinguished name. As for your filters, you’ll have to consult with your AD admin, or be capable of examining your LDAP structure to draw your own conclusions. Keep in mind that if you don’t fill in the fields for the ‘All Users’ and ‘All Groups’ filters, then the Users/Groups tabs will not be auto populated with the users/groups from AD.

Once you have this hooked up, you can start referencing groups that your Active Directory users are members of for security purposes. None of the actual steps in the process are necessarily difficult. What made this difficult for us was more to do with lack of complete and cogent documentation. This post isn’t sufficient on its own to get you where you need to be, but hopefully will help clarify some of what you will read on other pages. Good luck!

Have You Seen This SQL Server Bug?

I may have found a bug regarding how SQL Server calls CLR functions when using columns from tables joined using non-indexed columns. I’m including a SQL script, with comments on what to do with it, and hopefully anyone reading this will try it for themselves and tell me if what I’m seeing happens for them and whether I’m misinterpreting things. I’m willing to admit if I’m misunderstanding how things are supposed to work but so far everyone that I have spoken with about this is as perplexed as I am. And yet none of us can imagine this has never been noticed before and also is working as intended.

For starters, I discovered this in 64 bit SQL Server 2008 R2. One of my coworkers, our DBA, also reproduced this on 64 bit SQL Server 2012. I posted the issue at stackoverflow but no one even commented on it. My summary of the problem as I posted there is as follows:

SQL Server (at least on 2008 R2, 64 bit), when processing a query that includes a CLR function call in the SELECT clause and when using at least one column from a table that is included via JOIN on non-indexed columns, appears to invoke that CLR function for every row not constrained by either an indexed column in the JOIN or by an explicit WHERE clause item.

Now for some more details. This all has to do with the Microsoft spatial objects CLR functionality. Suffice to say I have a table full of extents, latitude/longitude pairs marking the opposing corners of a region. Additionally I have an SRID (spatial reference ID) to indicate what coordinate system was used. Some use meters offset, some use degrees. Also, the data I have is dirty. It has NULL values, invalid SRID values, coordinates that don’t match the SRID (i.e. meter offsets recorded for a supposedly degree based SRID). Finally, not only did I need to filter out potential offending values I also had additional filters being joined in from other tables. So my original SQL was actually doing a pre-filter pass to generate a valid set of ID values followed by the actual SELECT which joined this list of pre-filtered keys in a table variable with the original data table.

Part of the SELECT was to make use of the geography::STGeomFromText() call to return a geography object as a column in the result set. No sweat, my tests showed only valid rows were returning. But when I added that call, I began getting exceptions from the underlying CLR complaining about invalid geographies, invalid SRID values, insufficient numbers of points in the ring, etc. After a considerable amount of debugging and testing, I came up with the script listed below.

The first chunk of the script is commented out and includes two sections. One section creates a FUNCTION that takes a VARCHAR and an INT and just passes them directly to geography::STGeomFromText(). It is a simple wrapper. More on that in a bit.

The second section in the commented out area sets up the data table. The data table happens to include 7 test records but could probably work with fewer. I hit the exact results I was hunting down after I had added the 7th record and as much out of superstitious need to cling to my voodoo doll as anything else, I did not alter the data set. In any case, the CREATE TABLE is most notable for NOT indexing the ID column. The column is also not an IDENTITY but honestly making it an IDENTITY column did not impact the outcome and just added needless complexity.

The uncommented code that follows declares a table variable to hold valid keys, populates the table variable, and then performs a SELECT, returning not only all columns from the original data table but also additional columns which directly invoke the CLR function as well as call the UDF created up top. There is also a commented out WHERE clause.

I’ll summarize what I saw before I dump the script.

  • If I do not include the CLR call as a column in the SELECT, the SELECT works and I only see records which have valid values that would work in a call to the CLR function.
  • If I call the UDF, a wrapper around the CLR, I again only see the expected records and I get valid geography objects returned for each.
  • If I call the CLR, I receive an exception about an invalid SRID (being NULL). Depending on the results of the full table scan, you may also receive errors about invalid geography point values. Regardless, the query fails.
  • If I uncomment the WHERE clause, I can now include the CLR call as a column with no errors.
  • If the WHERE clause is commented out but the original data table is instead recreated such that the id field is a PRIMARY KEY field (or really, indexed in any way but the obvious approach would be to make it a PRIMARY KEY), the CLR function may also be included in the SELECT without issue.

What I believe is happening is that the main query is joining two tables, the data table and the table with a simple list of keys. It joins these tables on the ID column. When there is an INDEX on the data table’s ID column, SQL Server first joins on the results of the INDEX lookup, then fetches only the rows corresponding to the joined keys. Since the key table only has keys to rows with valid data, no invalid data is ever even seen. But when there is no INDEX, a table scan must be performed on the data table. While it is true that the result set will only end up including rows with valid data since that’s the only keys in the key table, it seems as though the CLR function is still being called for each row in the scan, regardless of whether it will end up in the final result set.

The interesting thing about the UDF is that it seems to mask whatever behavior is going on with the CLR function call. That is, the UDF does not get called for every row of a table scan, only for the rows which would be returned in the result set.

Aside from simple stating the obvious, that the lack of an INDEX forces a full table scan and the database is giving the CLR function a shot on each row before it is filtered out by the JOIN restriction (though, oddly enough, after any WHERE clause), I can’t see why this would happen. Wrapping it in a UDF seems to use a slightly different logic path. Perhaps it is a bug in the CLR assembly from Microsoft (this is Microsoft’s geography implementation) but I don’t have any other assemblies to test and our environment doesn’t allow us to install anything else.

Anyhow, here’s the script to reproduce the problem.

/*
 * This part of the script makes a UDF available to wrap the CLR call
 *
 * Only run this once
--
--
--
-- BEGIN RUN ONCE
--
--
--
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[fLPPTestMakeGeography]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
DROP FUNCTION [dbo].[fMakeGeography]
GO
CREATE FUNCTION [dbo].[fLPPTestMakeGeography]
(
    @p1 varchar(max)
    , @p2 int
)
RETURNS geography
AS
BEGIN
    DECLARE @Result geography
    -- just a wrapper around the geography::STGeomFromText call
    SELECT @Result = geography::STGeomFromText(@p1,@p2)
    RETURN @Result
END
GO
--
--
--
-- END RUN ONCE
--
--
--
*/
/*
 * Likewise this sets up the test table.
 * Note that this all goes away when a PRIMARY KEY is placed on the id column
 * Commentary: by making id a PRIMARY KEY, it causes the join in the main query
 * to do an index lookup rather than doing a full scan of the data table. Without
 * that PRIMARY KEY setting, even though the table variable is joined to the data
 * table in such a way as to eliminate any potential invalid rows, every row of
 * the data table gets examined. The fact that the CLR call throws an exception
 * in this case but the UDF call does not suggests the following:
 * For each row of a table that is not filtered in the JOIN clause via an index
 * and not filtered in the WHERE clause directly, any CLR call in the SELECT
 * clause will be called if it involves any columns from that row.
 * Note that this behavior differs from that of UDFs, where the UDF call only
 * takes place once *all* filtering is done, including non-INDEXed portions of
 * any JOIN statements.
--
--
--
-- BEGIN RUN ONCE
--
--
--
drop table dbo.LPPTestEnvelope
create table dbo.LPPTestEnvelope
(
id int not null
, geog int
, minx decimal(38,16)
, maxx decimal(38,16)
, miny decimal(38,16)
, maxy decimal(38,16)
)
insert into dbo.LPPTestEnvelope (id, minx, maxx, miny, maxy, geog)
select 1,-80,-70,20,30,4326 union all
select 2,-80,-80,20,20,NULL union all
select 3,-80,-75,20,25,4326 union all
select 4,NULL,NULL,NULL,NULL,4326 union all
select 5,-85,-70,25,40,4326 union all
select 6,NULL,NULL,NULL,NULL,4326 union all
select 7,-100000,-90000,3000000,3100000,4326
--
--
--
-- END RUN ONCE
--
--
--
*/
-- select * from dbo.LPPTestEnvelope
set nocount on;
-- in a more complex setting this table variable
-- would be used to do some complex pre-queries and hold the
-- key values which would be joined to the original data
-- table to get the actual rows desired
--
-- for this example it's just dumbed down but the concept holds
declare @envtbl table
(
    eid int not null primary key
)
INSERT INTO @envtbl (eid)
SELECT
    id
FROM
    dbo.LPPTestEnvelope env
WHERE
    -- exclude invalid SRID values
    env.geog=4326
    -- exclude invalid lat/long values (e.g. UTM values with improper SRID assignments)
    AND env.minx > -180 AND env.minx < 180 AND env.maxx > -180 AND env.maxx < 180
    AND env.miny > -90 AND env.miny < 90 AND env.maxy > -90 AND env.maxy < 90
    -- avoid precision bug
    AND ABS(env.maxx-env.minx)>1E-7 AND ABS(env.maxy-env.miny)>1E-7
    -- avoid crossing hemispheres
    AND SIGN(env.maxx)=SIGN(env.minx) AND SIGN(env.maxy)=SIGN(env.miny)
SELECT
    -- display the raw data from the table
    env.*
    -- directly invoke the CLR to create a geography object
    -- NOTE: COMMENT THIS COLUMN OUT TO MAKE THE QUERY WORK
    ,geography::STGeomFromText('POLYGON(('
            + CAST(env.minx AS VARCHAR) + ' ' + CAST (env.maxy AS VARCHAR) + ', '
            + CAST(env.minx AS VARCHAR) + ' ' + CAST (env.miny AS VARCHAR) + ', '
            + CAST(env.maxx AS VARCHAR) + ' ' + CAST (env.miny AS VARCHAR) + ', '
            + CAST(env.maxx AS VARCHAR) + ' ' + CAST (env.maxy AS VARCHAR) + ', '
            + CAST(env.minx AS VARCHAR) + ' ' + CAST (env.maxy AS VARCHAR)
            + '))', env.geog)
    ,[dbo].[fLPPTestMakeGeography]('POLYGON(('
            + CAST(env.minx AS VARCHAR) + ' ' + CAST (env.maxy AS VARCHAR) + ', '
            + CAST(env.minx AS VARCHAR) + ' ' + CAST (env.miny AS VARCHAR) + ', '
            + CAST(env.maxx AS VARCHAR) + ' ' + CAST (env.miny AS VARCHAR) + ', '
            + CAST(env.maxx AS VARCHAR) + ' ' + CAST (env.maxy AS VARCHAR) + ', '
            + CAST(env.minx AS VARCHAR) + ' ' + CAST (env.maxy AS VARCHAR)
            + '))',env.geog)
    ,'SELECT geography::STPolyFromText(''POLYGON(('
            + CAST(env.minx AS VARCHAR) + ' ' + CAST (env.maxy AS VARCHAR) + ', '
            + CAST(env.minx AS VARCHAR) + ' ' + CAST (env.miny AS VARCHAR) + ', '
            + CAST(env.maxx AS VARCHAR) + ' ' + CAST (env.miny AS VARCHAR) + ', '
            + CAST(env.maxx AS VARCHAR) + ' ' + CAST (env.maxy AS VARCHAR) + ', '
            + CAST(env.minx AS VARCHAR) + ' ' + CAST (env.maxy AS VARCHAR)
            + '))'',' + CAST(env.geog AS VARCHAR) + ')'
FROM
    -- join our data table to our table variable to fetch only the desired rows
    dbo.LPPTestEnvelope env
    INNER JOIN @envtbl et on et.eid=env.id
-- NOTE: UNCOMMENT THE WHERE CLAUSE TO MAKE THE QUERY WORK REGARDLESS OF INDEX
--WHERE
--  -- exclude invalid SRID values
--  env.geog=4326
--  -- exclude invalid lat/long values (e.g. UTM values with improper SRID assignments)
--  AND env.minx > -180 AND env.minx < 180 AND env.maxx > -180 AND env.maxx < 180
--  AND env.miny > -90 AND env.miny < 90 AND env.maxy > -90 AND env.maxy < 90
--  -- avoid precision bug
--  AND ABS(env.maxx-env.minx)>1E-7 AND ABS(env.maxy-env.miny)>1E-7
--  -- avoid crossing hemispheres
--  AND SIGN(env.maxx)=SIGN(env.minx) AND SIGN(env.maxy)=SIGN(env.miny)

Short Term Pain for Long Term Gain

A recent story that’s shown up a number of places concerns a Houston couple giving away around $4 billion for causes they deem worthwhile and, more importantly, that appear to be provably effective. The idea being that they want to make sure their dollars go toward things which have a higher likelihood of permanent change and for which metrics are in place or can be put in place to measure that effectiveness. Additionally they want proposals to have already thought things through, showing the path from getting to where the problem is now to where the solution may be. That said, supposedly there is some sort of backlash over this because they are spending their money on things which do not necessarily help people in need right now.

Let’s overlook the absurdity of telling them how they can spend their money for a moment. After all, if that were an issue we should be storming the gates of every rich person out there who lives the playboy/playgirl lifestyle and does precious little for their fellow human and we don’t see that. As far as I’m concerned the fact that this couple is spending any of their money on social reform is a good thing and to be lauded.

I dislike the argument that they should focus their efforts on short term solutions to long term problems, which is one aspect of the argument being made against their activities. To begin with there are already a lot of entities providing help to the needy in many aspects. Could more money help some of them? Absolutely. But wouldn’t it be better if, instead of pouring money into addressing the symptom you put money into finding a cure? If instead of simply buying more aspirin, you could stop the fever and watch it die out?

There are some, however, who don’t so much disagree with the idea of finding a permanent solution as they do with the hubris associated for daring to believe that some solutions can be found or even paid for. And yet, why not at least try? If nothing else some of the money spent on trying to find and enact a solution to a social ill could turn up information that leads to an actual approach that works or just increased awareness of the problem. Again, all of this is a positive outcome. At worst, the money simply disappears into a black hole with zero results. That would be unfortunate on many levels but not a catastrophe for anyone besides the funding couple.

Personally I am excited to see this approach taken. While I firmly believe that too much measurement can bog down a process or a system, I also believe the opposite end of the spectrum is adverse as well; too little measurement results in massive inefficiency. In this case the measurement isn’t even focused on whether an organization is spending their money on those in need, getting by on as little as possible, etc. It is solely focused on getting the most long term bang for the buck, seeing to it that the focus is on eliminating or at least scaling down the problem as much as possible rather than just treating the symptoms. If this works, and any effects would necessarily be felt only in the long term, it could become a blueprint for how to best go about addressing issues that have plagued mankind for far too long. I can’t wait to see how it turns out!

New Stalemate Update Is Available

Like it says on the tin, the new update for Stalemate is available, get it while it’s hot!

Also, it’s free.. well the update was free, but now I’ve made the app free too. And Centripetal as well! Y’all have fun!

Stalemate Updates Coming Soon

I’ve recently been revisiting my little iOS chess like app, Stalemate. The latest update is currently in review and includes two major revisions. The first and most important is the addition of a tutorial mode. That is a feature that has been requested and honestly should have been there from the word go. When I put Centripetal together, the game play was simple enough that a simple help screen was sufficient with some explanation of the goal and obstacles. With Stalemate the scoring needed to be explained, the fact that pieces don’t move once placed was probably not what folks expected and though simple it is still more complex than Centripetal. So yeah, should have made a tutorial to begin with.

The second and less important but more visible change is with the look and feel. I moved away from my attempt at aged parchment with some sort of Renaissance gradient font (seriously, what was I thinking?) to a simple black and white glossy look more in line with the icon set I used for the pieces in the first place. Here’s the new splash page:

Splash screen for Stalemate iPhone app

Splash screen for Stalemate iPhone app

Okay, yes, I’ve picked another custom font. But this time there is no gradient at least. When I created the reflection, at first I just made it a normal reflection but then it occurred to me to reflect (see what I did there?) the oppositional nature of the game and therefore made the reflected portion a negative of the full portion. I may not have a future as a graphic design artist but I thought it was a nice touch.

Update – We’re now live with the new look. In addition, I’ve made Stalemate and Centripetal free. I think I would rather see them getting played than continue to see a trickle of income off of them. Have fun!

Centripetal Re-released!

Greetings and salutations and welcome to 2013! For my first new post of the year I am very happy to announce that I have re-released Centripetal onto the iTunes App Store under my own name. 🙂 Click here to purchase Centripetal on the iTunes App Store.

As an aside, a fair bit has changed since I first released Centripetal. I had used cocos2d 0.99 at the time. XCode 3 was all the rage. iOS was at v4.x and OpenGL ES 1.0 was the standard.

Now cocos2d 2.0 is out, XCode is at 4.5, iOS is up to v6.x and OpenGL ES 2.0 is en vogue on our favorite mobile platform. As a result, it wasn’t enough to just recompile and resubmit. I ended up upgrading cocos2d, box2d, CocosDenshion and the other related libs. I also needed to tweak some of the code that I happened to have which made use of the GL_POINTS_SMOOTH parameter in the call to glEnable. By tweak I mean remove. You’ll see that on the help screens where the dot indicator showing which page you are on is now a square and not a circle. Yay progress?!

I do, however, want to also release this for OS X on the Mac App Store as well. Oddly enough I discovered it is enjoyable to play on the desktop due to running it in the iOS simulator.

Anyhow, if you already have your original copy, know that this is purely a re-release. If you haven’t tried it out before, you can check out my Centripetal page here (or click the link at the top of the page in the header) and watch the gameplay video.

Mitt-Math

Mitt Romney has been proclaiming his tax plan is designed to lower tax rates in a revenue neutral manner without reducing the share of taxes paid by the wealthiest Americans and without increasing the tax burdens on middle class and poorer Americans. His general theme includes several points:

  • Cut all tax rates by 20% in all brackets
  • Eliminate a number of deductions
  • Provide a boost to the economy to increase revenue through tax collection

Note this is not the same as his five point plan. I’m just talking about the general tax related portions. I don’t see how this works and I’m not the only one.

Imagine for a moment that the taxes collected by the IRS form a pie. A very small number of tax payers, the wealthiest 5% or so in America, provide 60% of that pie. Other tax brackets make up the remaining 40%. If you cut all tax rates by 20%, it would be akin to cutting off the outermost portion of that whole pie. The relative sizes of the slice remain the same but you have a smaller pie. He said he doesn’t want the rich to pay a smaller share of overall tax revenue, and this is in keeping with that. He says he wants middle class families to not have to pay so much money and this works there too. He wants to do this in a revenue neutral manner and now we have some problems. What next?

Thing is, the US government is pretty hungry and wants that pie. You’ve made it smaller. We’re not revenue neutral. So now we eliminate some deductions. This would add some pie back into the mix. But, and here’s a problem that Romney has yet to address, he has not told us what deductions he would suggest eliminating. That’s pretty important. At one point, he suggested possibly eliminating the mortgage interest deduction. While his campaign later backed off the idea, that would have been problematic as it would disproportionately affect the middle class he says he doesn’t want to hurt. How? Simple… the mortgage interest deduction represents, in relative terms, a larger write off for middle class households than upper class. Take that away and that family is now paying a higher amount of their own money in taxes than they were before. But it gets better.

I watched the debate last night and was surprised that Romney was pitching the idea of eliminating capital gains taxes as a boon to the middle class. The fact is that upper class earners would benefit far more than middle class earners if the capital gains tax were eliminated. So if this were pushed through, not only would middle class families not see much of a positive from this, now the taxes paid by upper class earners are going to go down, quite a bit. For the wealthiest earners, most of their income comes from capital gains, not from regular income. That is why their overall tax rate is so much lower than the rest of us. Eliminate taxes on capital gains and you eliminate taxes on the bulk of the money they earn. All of a sudden, not only is the pie much smaller but that 60% has shrunk a bit too.

The final piece of the puzzle is stimulating the economy. This is where Romney pitches his various plans to help create jobs and increase trade. With more money earned, more taxes will be paid. Great! Except capital gains are now off the table, so the business owners who are now making more money aren’t paying any more in taxes. So who is picking up the tab here? Oh, the middle and lower classes of income earners. The ones Romney says he doesn’t want to make pay more.

Now, whether you think it is fair that 5% of Americans are responsible for providing 60% of the tax revenue is an entirely different question. The point here is that Romney’s plan does not work as he says it does. And the biggest tell is that he refuses to provide any details. There’s a lot of hand waving but when it comes down to it, his plan is less of a plan than a wishlist. If he starts laying out details and those details make sense, I’ll be the first to agree that it is a workable solution. But until then all I’ve seen are smoke and mirrors.