explorers' club

explorations in dev, science, sci-fi, games, and other fun stuff!


9 Comments

Old School Trigonometry Tricks & Tips

Note to self:

I was cleaning out some old notebooks and file and found my old TI-83 Graphing Calculator from high school (circa 1994-ish).  Inside its slip cover I found some old sticky notes with my trig tricks and tips.

The trig triangle of choice:

This triangle will be referenced by most of the tips below.

Radians to Degrees and back again:

  • radians = degrees * π / 180
  • degress = radians * 180 / π

SOH CAH TOA

  • sin ϴ = opposite / hypotenuse
  • cos ϴ = adjacent / hypotenuse
  • tan ϴ = opposite / adjacent
  • cot ϴ = adjacent / opposite (reciprocal of tan ϴ)
  • sec ϴ = hypotenuse / adjacent (reciprocal of cos ϴ)
  • csc ϴ = hypotenuse / opposite (reciprocal of sin ϴ)

Subdivision of cirle in 30º increments:

  • 30º = π / 6
  • 60º = 2π / 6
  • 90º = 3π / 6
  • 120º = 4π / 6
  • 150º = 5π / 6
  • 180º = π
  • 210º = 7π / 6
  • 240º = 8π / 6
  • 270º = 9π / 6
  • 300º = 10π / 6
  • 330º = 11π / 6
  • 360º = 2π

Subdivision of cirle in 45º increments:

  • 45º = π / 4
  • 90º = 2π / 4
  • 135º = 3π / 4
  • 180º = π
  • 225º = 5π / 4
  • 270º = 6π / 4
  • 315º = 7π / 4
  • 360º = 2π

Cosine constants for common values:

  • cos 0º = 1
  • cos 30º = √3 / 2
  • cos 45º = √2 / 2
  • cos 60º = 1 / 2
  • cos 90º = 0
  • cos 180º = -1
  • cos 270º = 0

Sine constants for common values:

  • sin 0º = 0
  • sin 30º = 1 / 2
  • sin 45º = √2 / 2
  • sin 60º = √3 / 2
  • sin 90º = 1
  • sin 180º = 0
  • sin 270º = -1

Tangent constants for common values:

  • tan 0º = 0
  • tan 30º = √3 / 3
  • tan 45º = 1
  • tan 60º = √3
  • tan 90º = ∞
  • tan 180º = 0
  • tan 270º = ∞

Secant constants for common values:

  • sec 0º = 1
  • sec 30º = 2√3 / 3
  • sec 45º = √2
  • sec 60º = 2
  • sec 90º = ∞
  • sec 180º = -1
  • sec 270º = ∞

Cosecant constants for common values:

  • sec 0º = ∞
  • sec 30º = 2
  • sec 45º = √2
  • sec 60º = 2√3 / 3
  • sec 90º = 1
  • sec 180º = ∞
  • sec 270º = -1

Cotangent constants for common values:

  • sec 0º = ∞
  • sec 30º = √3
  • sec 45º = 1
  • sec 60º = √3 / 3
  • sec 90º = 0
  • sec 180º = ∞
  • sec 270º = 0

Law of Cosines:

  • a² = b² + c² – 2bc * cos α
  • b² = a² + c² – 2ac * cos β
  • c² = a² + b² – 2ab * cos γ

….formulated to solve for the cosines:

  • cos α = (b² + c² – a²) / 2bc
  • cos β = (a² + c² – b²) / 2ac
  • cos γ = (a² + b² – c²) / 2ab

Law of Sines:

Given a circle with a radius = r circumscribed around a triangle,

  • sin α / a = 2r
  • sin β / b = 2r
  • sin γ / c = 2r

and the rest that I couldn’t make sense of:

Some inversions of the advanced stuff I never use:

  • arcsec ϴ = arccos (1 / ϴ)
  • arccsc ϴ = arcsin (1 / ϴ)

Some domain, range and quadrant stuff regarding f(x) = y:

  • y = tan^-1 x:
    • Domain: all real numbers
    • Range: -π / 2 – π / 2
    • Quadrants: I, IV
  • y = cot^-1 x:
    • Domain: all real numbers
    • Range: 0 – π
    • Quadrants: I, II
  • y = sec^-1 x:
    • Domain: x ≥ 1, x ≤ -1
    • Range: 0 – π
    • Quadrants: I, II
  • y = csc^-1 x:
    • Domain: x ≥ 1, x ≤ -1
    • Range: -π / 2 – π / 2
    • Quadrants: I, IV


1 Comment

Bitmap Fills oriented for Isometric Projection

Notes to self…

Given an image suitable for use as a bitmap fill, certain matrix transformations need to be done in order to orient the image to the xy, xz, and yz planes in the xyz octant.  Assume the following octant orientations:

myIso

Here are the following matrix tranformations:

  • XY plane orientation – this is the equivalent to rotating the image by 45º and then reducing the height by half the original scale.

    var m:Matrix = new Matrix();
    m.rotate(Math.PI / 4);
    var m2:Matrix = new Matrix();
    m2.scale(1, 0.5);
    m.concat(m2);

  • XZ plane orientation – this is simply skewing the image in the flash-based coordinate system along the  y axis.  Since most isometric projections (including the as3isolib) use a 2:1 ratio, we use Math.atan(0.5) rather than Math.PI / 6.

    var m:Matrix = new Matrix();
    m.b = Math.atan(0.5);

  • YZ plane orientation – same as the XZ plane however we use the skew value * -1;

    var m:Matrix = new Matrix();
    m.b = Math.atan(-0.5);


25 Comments

as3isolib alpha released

Yep its out there.  Yep its an alpha release which means that APIs, class names and basic project details are subject to change.  This release includes the source files, a SWC and docs.  I also added two of many tutorials to get people started.

as3isolib alpha release – link
as3isolib Google Code project page – link

Please feel free to send me questions, comments, criticisms, death threats, etc.  All input is welcome.

Thanks.
J


7 Comments

Absolute Isometric Depth Sorting

[update – 2010.01.27]

This should have been updated ages ago however I haven’t gotten around to it sorry 😦

A solution was found, check out the as3isolib’s DefaultSceneLayoutRenderer

[update – 20080909]

As I suspected, it was too good to be true.  More testing is necessary, however I feel I am on the right path.

[update – 20080908]

Finally did it!!!  Or at least it appears that I have accomplished my goals through initial testing phases.  I’ll restate those goals for clarity:

  • absolute isometric depth sorting – position & size determine depth without the aid of a tile based system.
  • objects residing at higher depths are still sorted by their intended visual placement including the possibility of appearing behind objects in the foreground whose heights may extend above the plane of the higher object.

Keep in mind this has only been tested against the case found in the previous updates where one object residing on a higher plane is tested against one object residing at a lower plane whose height is above the first object.  Further testing needs to take place in order to ensure this remains solid when multiple object are tested against one another.  Here is a quick rundown of a render phase:

  • container checks children for any size, depth, or geometry invalidation.  If so set a flag for further logic.
    • sort children by the following order:
      • elevation plane
      • isometric x value
      • isometric y value
    • if more than one elevation plane exists (simple check does children[0].z != children[children.length – 1].z), then we do a recursive manual sort by:
      • if A == B, continue
      • if A is intended to appear behind B (by performing a few simple position/geometry checks) then place B to A’s index + 1.
      • repeat loop
    • set each child’s depth to their index value within the sorted children.
  • call render on all children

[update – 20080907]

Ok so I made a big breakthrough.  I almost have it, I just need to refine the sorting algorithm just a tad bit more and it will be ready for a new round of testing.  Check out the screen shots (these are actual screen shots of the SWF, whereas the others from the earlier update were done using Sketchup):

The following 3 shots are obviously the good ones.  This test swf is animated with the blue box moving clockwise INSIDE the perimeter of the white boxes area and the orange box moving clockwise OUTSIDE the perimeter of the white boxes area.

Now here is the kink where I need to refine the sorting algorithm.  If the blue box’s screen y value is greater than the orange box’s screen y value, you don’t get the correct overlap.  Just a bit more tweaking and it should be good.

[update – 20080905]

After reaching out to a few contacts I thought I had made some progress.  Before I get started on this maybe I should post an image of what the problem is.  This is what I want to do:

Notice how the orange box is at a higher elevation than the gray boxes?  But using my current depth sorting algorithm the front-most gray box gets rendered behind the orange box like this:

My current algorithm slices up the scene based on elevations into an array.  The first array element would contain an array of all the gray boxes, the second element would contain an array with one element, the orange box.   Then I would sort both elements from front to back and order there depths recursively.  The issue with this sorting is that is still 2D depth sorting for a quasi 3D scene.

Any suggestions?

[update – 20080904]

So I kinda realized that this is great if you plan to not have stacked isometric objects.  In other words this works great if you are on a flat terrain map.  However this does not address issues with variable terrain (e.g. Diablo, Marble Madness).

I am still in the process of finding a smart, performance-friendly solution for this.  I am also thinking about setting a property on any container-type class that requires depth sorting to be able to use either the flat terrain approach (found here) or a variable terrain approach in order to maximize performance.

Thoughts?

[original post]

Recently I have been working on my Flash-oriented projects.  Specifically I started working again on isolib, an AS3 isometric library.

Used to in AS2 you could delegate the task of depth sorting to the flash player by doing some calculations and assingning a number without regards to wether or not a depth preceeding it was filled.  For example, I have 5 Sprites.  In AS2 you could assing Sprite #1 to depth 0, Sprite#2 to depth 50 and the other 3 Sprites could have arbitrary numbers assigned to them.  The flash player took on the task of ordering the display list for you.  In AS3 that is not the case as you will run into Run Time Errors if you try to assing a DisplayObject to a depth without the prior depths being occupied.  So now the task is assigned to the developer.

Now as many ways as there are to skin a cat, there are as many ways to do isometric depth sorting.  Almost all that I have found utilize tile-based algorithms.  Though they work with respect to tile-based applications, they don’t seem to do that great for absolute positioning.

Anyway to cut to the fat here is how I achieved Absolute Isometric Depth Sorting without the need to have tile-based logic assigned.  Before I jump in tho, my coordinate space is somewhat diffenent than that of traditional isometric coordinate spaces: x values go from the top-left to the bottom-right portion of your screen, y values go from the bottom-left to the top-right portion of your screen and z values go from bottom to top of your screen.  Make sense?  Okay, let’s go:

Almost any renderable element in my iso-world has to have x, y, z, width, length, and height depending on how many dimensions they occupy (2 vs 3 dimensions is like comparing a plane to a cube).  Let’s assume we are dealing with a cube.  Now since we the viewer represent the vantage point, I created a reference point to compare distances to which resides at x:100000, y:100000 and z:100000 (hopefully we will not exceed any points outside of that).  My sorting method simply compares distances from the right-front-most point of an object from the reference point.  That point is basically x:cube.x + cube.width, y:cube.y + cube.length, z:cube.z Now this is not fail safe but it does a good job in absolute coordinate spaces.

Obviously there is some more robust logic occurring in my IsoScene class in order to depth-sort but this is the basis for the sort.  Check out the demo app here – link

override public function render (recursive:Boolean=true):void
{
	var depthArray:Array = [];
	var infoObj:Object = {};

	var child:IPrimitive;
	for each (child in children)
	{
		infoObj = {};
		infoObj.child = child;
		infoObj.distance = Math.sqrt((camera.x - child.right) * (camera.x - child.right) +
			(camera.y - child.front) * (camera.y - child.front) +
			(camera.z - child.z) * (camera.z - child.z)) * -1;

		depthArray.push(infoObj);
	}

	depthArray = depthArray.sortOn(["distance", "x", "y", "z",], Array.NUMERIC);

	var i:int;
	for each (infoObj in depthArray)
	{
		if (infoObj.depth != i && (infoObj.child.positionInvalidated || infoObj.child.geometryInvalidated))
			setChildIndex(infoObj.child, i);

		i++;
	}

	super.render(recursive);
}


3 Comments

EventDispatcherProxy & ProxyEvent

Preface

This is gonna be a ‘stream of conscientiousness’ type post. Hopefully it will be rather easy to follow and not long winded.

Might want to open this and follow along: example flex application

In the Beginning

One of my pet projects has been making an isometric (i.e. Diablo, Diablo II) game for Flash. Part of that effort was to create an Isometric framework where a developer could easily create and manipulate isometric primitive objects while not having to deal with complex math or all the logic behind Isometric game engines. Suffice it to say its still in development and in the umpteenth version. Thank you OCD.

Initially I had made the framework in AS2, extending MovieClip, blah blah blah. Then AS3 came out and resolved alot of issues I was having while creating new ones. IsoLib made the jump to AS3. Now the basic unit was extending Sprite. That was great in one respect but generated alot of funky API methods that seemed counterintuitive.

At a crossroads

Recently I discovered Sandy 3D (or for those who can read Chinese, 3D 3D). What a phenomenal framework. I am completely blown away by it. Aside from the crazy parameter names in the API, it works beautifully. I haven’t fully decided to abandon my IsoLib efforts but this discovery certainly makes the idea more enticing. But for now I will continue trudging onwards as I think Sandy 3D may be a bit overkill or what I am doing. But peering into the framework, I discovered some things I was doing wrongly.

Mainly my basic visual unit was extending Sprite rather than being a data-type class with a stored reference to its visual attribute. Why would one choose this route? Well firstly it would make the framework’s API quite a bit lighter. Example: Rather than setting an IsoCube’s isoX or isoHeight, one could simple say isoCube.x or isoCube.height. So it certainly would allow a more intuitive API without rewiring Sprite’s public attributes. But it creates a problem.

Now that my visual class is hidden behind the scenes, I could not make use of events triggered on the non-public container aka. the base unit’s visual attribute rendered to the screen. So if I wanted to assign a click event to my isoCube, normally I would have to say isoCube.container.addEventListener… and again folks, container is non-public. Plus that again is not intuitive. I’d rather have a simpler API where I could say, isoCube.addEventListener… and it would do the dirty work behind the scenes. The other reason is that I want to the event target that is handled to be isoCube, not isoCube’s container. Don’t ya love OOP?

Enter EventDispatcherProxy & ProxyEvent

Proxy means alot of different things to alot of different people. My browser proxy, my proxy settings, etc., etc. But looking at a dictionary definition it simply means something which acts on behalf of something else. So this is an excellent name. Basically I want a class that will act as an IEventDispatcher on behalf of a non-public attribute who is also an IEventDispatcher.

Disclaimer

Now keep in mind I made this little thing in a day, and it is completely plausible that I am overlooking some one-liner AS3 trick that does all this for you. If so, please let me know.

How it works

EventDispatcherProxy is an IEventDispatcher which contains a few other items in it. It has:

  • public var proxy:IEventDispatcher – this is normally the instance, but you could dispatch on behalf of someone else too
  • public var proxyTarget:IEventDispatcher – the target which you are acting on behalf of
  • a non-public hash table to keep track of all the things you are intercepting
  • a redispatcher method that intercepts proxy’d events and triggers the appropriate handler function with the event target being he EventDispatcherProxy not the proxyTarget

ProxyEvent extends a regular ol’ Event with two additional properties:

  • public var proxy:IEventDispatcher – this will reference the instance of the EventDispatcherProxy, in the case of the IsoCube example above, this would be the IsoCube itself
  • public var targetEvent:Event – this is a reference to the original event that got intercepted and proxy’d for.

Its all theory for now or is it…

I am posting a zipped project for you to check out. Please give me some feedback.

example flex application

Thanks,
J


4 Comments

For those who thought Geometry sucked in high school…

This kind of stuff is what inspires me to go back to school and get a grad degree in…Math? Geometry?

The video is rather lengthy but its put into steps via simple animations so as to understand fully the process for inverting a sphere composed of a theoretical material that can infinitely pass through itself, bend, stretch and twist but cannot be pinched, torn, cut, or pierced.

[UPDATE – per a reader’s request/inquiry]

Not Knots – part 1

Not Knots – part 2