Experimental forum for "Technology" discussions (computers, cameras, etc).

Simple (and Primitive) 3D Rendering Program

Postby Bob Kuczewski » Mon Oct 17, 2022 11:28 pm

Quite some time ago I wrote a Javascript program to facilitate simple glider design. It had fields for several glider measurements, and it could use those fields to build and display a reasonably good looking result. You can find that program at https://ushawks.org/designer/.

While that program can do a reasonable job of rendering somewhat standard gliders, it was no match for some of the innovations recently appearing in the 5 foot packed topic. The program was too closely tailored for "traditional" hang gliders to handle so many novel ideas. As a result, I just finished a newer version that is very simple (primitive) but doesn't impose any restrictions on what can be designed. As with the other version, the design files could be fairly easily imported into other tools (like Blender) to produce more professional and appealing results.

The primary use case for this program is for sharing quick sketches of ideas using a fairly simple and freely available tool. The attached program (Frame_Designer_1.zip) is my first effort at making such a program. The zip file contains 4 files:

      frame_designer.htm
      header_bg.png
      site_logo.png
      board_bg.png

You should be able to unzip that zip file into any location on your computer and then double click on "frame_designer.htm" to open it in your web browser. The program will start up looking something like this:

Frame_Designer_1.png
Frame_Designer_1.png (182.09 KiB) Viewed 2122 times


You can click anywhere in the black display area and drag your mouse to rotate the glider in combined azimuth and elevation. If you just want to rotate it (in azimuth or in pitch) separately, you can click and drag the two red tabs to isolate the rotation around one of those two axes.

There are a number of buttons for quickly selecting a standard orthographic view (top, bottom, left, right, front, rear) as well as a "standard" oblique perspective. You can also show the 3D coordinate axes (X, Y, and Z drawn as red, green, and blue respectively). There's also a "Mirror X" button so you only have to build half of a symmetric model and the computer will mirror that half across the Y/Z plane. This is very useful since most gliders are symmetrical across that plane (this program uses the convention that the X axis extends out the right wing, the Y axis extends forward, and the Z axis points "up". There are also fields for specifying particular viewing angles and zoom values.

The actual design is done by specifying individual lines to be drawn in 3 dimensional space. Each line is specified by 2 end points, a line thickness, and a line color. That's it. These are grouped using the JSON array notation (with square brackets and commas between items). For example, the glider shown above was specified with this JSON description:

[
[ 0, 5, 0, 0, -5, 0, 4, "FF00FF" ], // Keel
[ 0, 5, 0, 15, -3, 0.2, 6, "FFFF00" ], // Leading Edge
[ 0, 1, 0, 0, 0.9, 3, 6, "0000FF" ], // Kingpost
[ 0, 1.6, 0, 9, 0.2, 0.1, 6, "00FFFF" ], // Crossbar
[ 0, 1, 0, 1.6, 2, -3.5, 4, "FF0000" ], // Downtubes
[ 0, 2, -3.5, 1.6, 2, -3.5, 4, "FF0000" ], // Basetube (right half)
[ 0, 5, 0, 1.6, 2, -3.5, 1, "888888" ], // Nose Wire to BT
[ 0, -3, 0, 1.6, 2, -3.5, 1, "888888" ], // Rear Wire to BT
[ 9, 0.2, 0.1, 1.6, 2, -3.5, 1, "888888" ], // Side Wire to BT
[ 9, 0.2, 0.1, 0, 0.9, 3, 1, "888888" ], // Side Wire to KP
[ 0, 5, 0, 0, 0.9, 3, 1, "888888" ], // Nose Wire to KP
[ 0, -3, 0, 0, 0.9, 3, 1, "888888" ], // Rear Wire to KP
]

These fields are:    [    x1,y1,z1,    x2,y2,z2,    thickness,    "RRGGBB"    ]

The commas are important and must be entered as shown between individual numbers and after the closing square bracket of each entry. All of the text on each line following the "//" is ignored by the program. That text is called a "comment field" and it's there to help people understand what all the numbers mean. The computer ignores the comments completely.

As shown, the first three numbers specify the first end point of each line segment in 3 dimensional space (x,y,z). The second three numbers specify the second end point of each line segment in 3 dimensional space (x,y,z). The seventh number is an integer line thickness for the line. Since the program is often used to draw tubes, the thickness can represent the thickness for each tube. The last value is a string of "RRGGBB" values which specify a color by combining different amounts of red (RR), green (GG), and blue (BB). The values for each of the three colors must be expressed in hexadecimal from 00 through FF inclusive. If you're not familiar with hexadecimal, just imagine that the letters A, B, C, D, E, and F are progressively larger versions of the 0 to 9 digits that you already know. So "FF" is the largest value and "00" is the smallest. The numbers "77" and "80" are about in the middle of that range. If you look at the example above, you'll notice that all of the values are either "00" or "FF" or "88". While you have much finer color resolution available, These "min", "max", and "medium" values will usually work just fine. For example, the color "red" would have the maximum value (FF) for the red (RR) part, and the minium (00) for the green (GG) and blue (BB) parts. So full red would be written as "FF0000". It turns out that the color "magenta' (bright purple or pink) is a full combination of red and blue. So magenta would be specified as "FF00FF" with no green. Similarly, yellow is made up of both red and green, so it would be specified as "FFFF00" (no blue). Note that the letter values (A,B,C,D,E,F) can also be entered in lower case (a,b,c,d,e,f).

The program comes with a few examples that you can use to get started. Here's the code for a simple green pyramid:

[
[ -5, -5, -5, 5, -5, -5, 1, "44ff44" ], // Base -X to +X
[ 5, -5, -5, 5, 5, -5, 1, "44ff44" ], // Base -Y to +Y
[ 5, 5, -5, -5, 5, -5, 1, "44ff44" ], // Base +X to -X
[ -5, 5, -5, -5, -5, -5, 1, "44ff44" ], // Base +Y to -Y

[ -5, -5, -5, 0, 0, 5, 1, "44ff44" ], // Base -X -Y to Top
[ 5, -5, -5, 0, 0, 5, 1, "44ff44" ], // Base +X -Y to Top
[ 5, 5, -5, 0, 0, 5, 1, "44ff44" ], // Base +X +Y to Top
[ -5, 5, -5, 0, 0, 5, 1, "44ff44" ], // Base -X +Y to Top
]

Here's an animation generated from that simple code:

Frame_Designer_1_Pyramid_36frames.gif
Frame_Designer_1_Pyramid_36frames.gif (169.26 KiB) Viewed 2122 times


If you observe that animation for a while, you may find yourself seeing it revolving both directions (clockwise and counter-clockwise from the top). That perception comes from the fact that the figure really is ambiguous because it lacks perspective, it lacks solid faces, and it lacks hidden line removal. However, even with those limitations, the program can be very helpful in understanding a 3 dimensional object that might otherwise be drawn in only 2 dimensions.

Mostly, this program is intended to help people better share their ideas with one another. If you have an idea for something that's hard to describe in words or difficult to express in static pictures, that idea might be a candidate for a simple model or animation. If so, the attached program could help you build one and share it with others here on the forum. Just build your model, copy the text from the "JSON" area, and post the JSON to the forum. Anyone who want's to see the animation or use it to build on can download it and work on it for a while and then post their own version to share again.

 
 
Attachments
Frame_Designer_1.zip
(87.17 KiB) Downloaded 144 times
Join a National Hang Gliding Organization: US Hawks at ushawks.org
View my rating at: US Hang Gliding Rating System
Every human at every point in history has an opportunity to choose courage over cowardice. Look around and you will find that opportunity in your own time.
Bob Kuczewski
User avatar
Contributor
Contributor
 
Posts: 8371
Joined: Fri Aug 13, 2010 2:40 pm
Location: San Diego, CA

Re: Simple (and Primitive) 3D Rendering Program

Postby Bob Kuczewski » Tue Oct 18, 2022 7:00 am

As another example, here's a model that was made using the program to build a leafless tree:

Tree_Rotating_1.gif
Tree_Rotating_1.gif (392.45 KiB) Viewed 2113 times
Join a National Hang Gliding Organization: US Hawks at ushawks.org
View my rating at: US Hang Gliding Rating System
Every human at every point in history has an opportunity to choose courage over cowardice. Look around and you will find that opportunity in your own time.
Bob Kuczewski
User avatar
Contributor
Contributor
 
Posts: 8371
Joined: Fri Aug 13, 2010 2:40 pm
Location: San Diego, CA

Re: Simple (and Primitive) 3D Rendering Program

Postby JoeF » Tue Oct 18, 2022 8:39 am

Nice! I hope to master the program. Thanks!
Here is a still outtake from something in the program:
aFramerStillOuttake001.jpg
aFramerStillOuttake001.jpg (33.05 KiB) Viewed 2110 times

Trying to get a .gif to show from the program ....
[
[ 0, 5, 0, 0, -5, 0, 4, "ff00ff" ], // Keel
]
No, that did not make a .gif show. Still seeking solution ...
Trying another path:
downloadJson.gif

No, that did not work to give active gif. What to do?
Trying png save; will it work?
downloadPNG.png

No. That did not transfer an active gif, just a still gif.
What to try now?
Code: Select all
[
 [ 0, 5, 0,    0, -5, 0,     4,   "ff00ff" ], // Keel
 [ 0, 5, 0,   15, -3, 0.2,   4,   "ffff00" ], // Leading Edge
]


"Bob Kuczewski wrote:copy the text from the "JSON" area, and post the JSON to the forum.

Would you expand on this instruction, please. I post some efforts that do not work. Thanks.
Join a National Hang Gliding Organization: US Hawks at ushawks.org

View pilots' hang gliding rating at: US Hang Gliding Rating System
JoeF
User avatar
Contributor
Contributor
 
Posts: 4549
Joined: Sat Aug 14, 2010 3:41 pm

Re: Simple (and Primitive) 3D Rendering Program

Postby Bob Kuczewski » Tue Oct 18, 2022 10:22 am

Hello Joe,

I made the animated GIFs by saving consecutive frames and combining them using the "GIMP" program. I'm sorry if anything I wrote indicated otherwise.

I made the animated GIFs for the purpose of showing how the program can give many dynamic views of your model. But if producing animated GIFs becomes a needed part of the program, then I might be able to add it.

Also, I posted the "tree" example at 7am this morning. That was before I got to bed, and I am sorry if my instructions were not clear. The main work of using the program is coming up with the coordinates that make up your object. That process usually involves a sketch with an estimation of coordinates. Then the coordinates can be entered, viewed, and adjusted. The resulting JSON model can be posted for others to use in the same program or converted to other forms for other purposes and presentation modes (like the animated GIF format).

JoeF wrote:
"Bob Kuczewski wrote:copy the text from the "JSON" area, and post the JSON to the forum.

Would you expand on this instruction, please. I post some efforts that do not work. Thanks.

When you build a model of whatever you want to show, you will have that model as text in the JSON window. You can copy that little bit of text to the forum in a code box (as you've done) so that other people can copy from your code block and paste into the JSON window of their own program. This will let them see and explore the 3D model that you created. They may add changes and repost their version for you to copy into your program for further thought and exploration. That could produce greater collaboration than sharing still images or even animated GIFs. That last point is important because an animated GIF of a model is just one person's sequencing of views. They make nice "eye candy", but they don't let the viewer examine the scene from their own perspective and following their own curiosity. So I believe that the animated GIFs are a poor substitute for exploring the scene from within the program itself.
Join a National Hang Gliding Organization: US Hawks at ushawks.org
View my rating at: US Hang Gliding Rating System
Every human at every point in history has an opportunity to choose courage over cowardice. Look around and you will find that opportunity in your own time.
Bob Kuczewski
User avatar
Contributor
Contributor
 
Posts: 8371
Joined: Fri Aug 13, 2010 2:40 pm
Location: San Diego, CA

Re: Simple (and Primitive) 3D Rendering Program

Postby JoeF » Tue Oct 18, 2022 10:51 am

Hope you get sleep needed!
Thanks for the extra comments!
Ah! So, you grabbed stills and built an animated .gif file. OK. Now, I see what you did to share the animated .gif in forum.
The Viewer shows any view that it allows after the object is coded; that is fun.
Join a National Hang Gliding Organization: US Hawks at ushawks.org

View pilots' hang gliding rating at: US Hang Gliding Rating System
JoeF
User avatar
Contributor
Contributor
 
Posts: 4549
Joined: Sat Aug 14, 2010 3:41 pm

Re: Simple (and Primitive) 3D Rendering Program

Postby Bob Kuczewski » Tue Oct 18, 2022 11:04 am

JoeF wrote:The Viewer shows any view that it allows after the object is coded; that is fun.


Exactly. I wrote the program with these images in mind:

file-49.jpg
file-49.jpg (15.03 KiB) Viewed 2100 times

file-53.jpg
file-53.jpg (12.67 KiB) Viewed 2100 times

file-47.jpg
file-47.jpg (46.47 KiB) Viewed 2100 times

file-56.jpg
file-56.jpg (29.56 KiB) Viewed 2100 times

I wanted to be able to understand your drawings better, and I thought a 3D model would help. There are a number of 3D programs out there (like Blender), but they all present a significant installation and learning challenge. I was hoping my program would be easier, but it also presents a serious challenge with regard to transcribing an idea into a series of 3D line segments. However, the good news is that those 3D segments from a model can be processed to feed other tools which might produce real high quality 3D output (such as Blender rendering) as well as plans and even manufacturing drawings for CAD/CAM production. All of those things can flow from a numerical 3D model.
Join a National Hang Gliding Organization: US Hawks at ushawks.org
View my rating at: US Hang Gliding Rating System
Every human at every point in history has an opportunity to choose courage over cowardice. Look around and you will find that opportunity in your own time.
Bob Kuczewski
User avatar
Contributor
Contributor
 
Posts: 8371
Joined: Fri Aug 13, 2010 2:40 pm
Location: San Diego, CA

Re: Simple (and Primitive) 3D Rendering Program

Postby JoeF » Tue Oct 18, 2022 2:07 pm

Anyone with the US Hawks Hang Gliding viewer loaded into a compatible browser may copy and paste the code into the JSON text field and press the "Load from JSON" to see the beam and line deal without yet king post or TCF and their related rig lines. The following code is my first effort for sharing using the program viewer; Bob Kuczewski made for anyone to use. Tweaks would improve. But the viewer will let one see generally what I mean as the planar deal that would hold the sail SS or the DS using sock. In the program, use the mirror button to toggle from half glider to get the symmetrical reflected other half. This code gives no sweep, but a TE washout. Coding for some sweep would be a next effort. Treat the numbers as 1= 1 ft for scaling for beams. The line weight integers are not feet, but some other measure; such gives thickness to the line in the graphic.

The intent for BEF014ASockA is that there could be no frame beams or ribs between the front spar and the rear spar, excepting the keel. The sail (SS or DS) would be tensed by the shown rigging lines. The present coded drawing does not indicate the intended taper in spar from keel to wing tip; intended is a three-piece telescopic spar where each piece is 5 ft long; extended there would be 6 inches of same-axis overlap telescopically; mass per foot would reduce jerkingly to the tip from keel.

Code: Select all
[
 [  15, 0, 0,       0,0,0,     6,   "00FF00" ], // Front spar
 [  15, -6, 0.1,    0,-6,0,    6,   "00FFFF" ], // Rear spar with washout
 [  0, 4, 0,        0, -10,0.5,    7,  "FF4433" ], // Keel with high aft
 [  5, 0, 0,        0, 4, 0,      1,  "FF4433" ], // Front spar stay line
 [  10, 0, 0,       0, 4, 0,     1,  "FF4433" ], // Front spar stay line
 [  13, 0, 0,       0, 4, 0,    1,   "FF4433" ], // Front spar stay line
 [  5, -6, 0,       0, -10, 0.5,      1,  "FF4433" ], // Rear spar stay line
 [  10, -6, 0.4,      0, -10, 0.5,     1,  "FF4433" ], // Rear spar stay line
 [  13, -6, 0.5,      0, -10, 0.5,    1,   "FF4433" ], // Rear spar stay line
]
Join a National Hang Gliding Organization: US Hawks at ushawks.org

View pilots' hang gliding rating at: US Hang Gliding Rating System
JoeF
User avatar
Contributor
Contributor
 
Posts: 4549
Joined: Sat Aug 14, 2010 3:41 pm

Re: Simple (and Primitive) 3D Rendering Program

Postby Bob Kuczewski » Tue Oct 18, 2022 5:31 pm

Very Nice Joe!! I was able to load your model and display it just fine. Your comments were helpful when I merged it with my control bar and wires from an earlier model. The new version is in "The 5 ft-packed-HG Movement" topic.

While working on this project, I found that adding comments to a model really really helps. Even just a word like "keel" or "downtube" or "crossbar" can help make things much clearer.
Join a National Hang Gliding Organization: US Hawks at ushawks.org
View my rating at: US Hang Gliding Rating System
Every human at every point in history has an opportunity to choose courage over cowardice. Look around and you will find that opportunity in your own time.
Bob Kuczewski
User avatar
Contributor
Contributor
 
Posts: 8371
Joined: Fri Aug 13, 2010 2:40 pm
Location: San Diego, CA

Re: Simple (and Primitive) 3D Rendering Program

Postby Bob Kuczewski » Tue Oct 18, 2022 7:44 pm

JSON

In my earlier post, I mentioned "JSON", but I didn't explain it. "JSON" stands for "JavaScript Object Notation", and it's just a fairly simple way to describe many kinds of data and data structures. It's patterned after the data structures used in the Javascript programming language (where it obviously got its name). You can read about JSON at JSON.org and Wikipedia.

In a nutshell, JSON provides a specification for exchanging numbers, text, sequential lists of other things, and indexed lists of other things. The JSON specification is about the simplest specification I've ever seen in the field of computing. In fact, I'd say it's refreshingly simple.

The definitions for numbers and text ("strings") are pretty much standard, so I won't belabor them. But the specifications for sequential lists ("Arrays) and indexed lists ("Objects") deserve a little more discussion.

Arrays (sequential lists of things)

A JSON Array is a list of other "things" inside a pair of square brackets ( [...] ). The things inside have to be separated by commas. So here's a list of numbers [3, 1, 4, 1, 9]. That's pretty simple. In that case all of the values in the Array were numbers. But an Array can also contain strings like this ["three", 1, "four", 1, 9]. But where things really get interesting is when an Array contains other Arrays. For example, we might use Arrays to store a few 3D coordinates (x, y, and z). So here's the origin: [0, 0, 0]. Here's a point 7 units above the origin: [0, 0, 7]. Here's a point 2 units to one side of the origin, and 11 units down: [2, 0, -11]. Could we store those three Array points in yet another Array? Yes. Here's what that would look like:

       [ [0, 0, 0], [0, 0, 7], [2, 0, -11] ]

That's 9 numbers stored as 3 groups of three. We can specify which one we want by an "index number" into each group or subgroup. In other words, we find data in an Array by its position in the array (first, second, third ... last).

Objects (named lists of things)

A JSON Object is very similar to a JSON Array, and they can both store "things". But while an array stores things by sequential position, Objects store things by a named index. So to store a person's age inside of an object, you might name it something like "Age". Then you could ask for the "Age" value stored in that particular object. Here's how you might create an oject to store three simple items:

       { "name": "Bob",   "age": 39,   "weight": 175" }

Just as with Arrays, an Object can can contain other data structures as well. So an Object could contain another Object which in turn contains an Array of more Objects and Arrays. It's all a matter of which representation is the best fit for the data being stored.

Back to the point ...

The Simple and Primitive 3D rendering program expects to get its data in the form of single JSON Array containing other JSON Arrays. Each of these inner arrays typically holds the information to draw a single line segment. This is shown here in this example from an earlier post by Joe:

[
[ 15, 0, 0, 0, 0, 0, 6, "00FF00" ], // Front spar
[ 15, -6, 0.1, 0, -6, 0, 6, "00FFFF" ], // Rear spar with washout
[ 0, 4, 0, 0, -10, 0.5, 7, "FF4433" ], // Keel with high aft
[ 5, 0, 0, 0, 4, 0, 1, "FF4433" ], // Front spar stay line
[ 10, 0, 0, 0, 4, 0, 1, "FF4433" ], // Front spar stay line
[ 13, 0, 0, 0, 4, 0, 1, "FF4433" ], // Front spar stay line
[ 5, -6, 0, 0, -10, 0.5, 1, "FF4433" ], // Rear spar stay line
[ 10, -6, 0.4, 0, -10, 0.5, 1, "FF4433" ], // Rear spar stay line
[ 13, -6, 0.5, 0, -10, 0.5, 1, "FF4433" ], // Rear spar stay line
]


The whole thing is one Array contained between the opening and closing square brackets ( [...] ). Each line in that one (outer) Array is yet another (inner) Array. And each of those inner Arrays contains a starting point for a line segment (first 3 numbers), an ending point for a line segment (second 3 numbers), a number to specify the drawing width (7th number), and a string to specify the color (the last item in each inner Array). That's the layout of the data used to generate the line segments that make up a drawing in this program.

Now, since I wrote the program, I could have chosen any layout that I wanted. For example, I could have made each of the starting and ending points into an Array of 3 values. In that case, the first row would have looked like this:

[  [15, 0, 0],      [0, 0, 0],      6,    "00FF00" ],  // Front spar


I could also have used an Object for each line segment (note the names, colons, and the "curly braces" in this representation):

{ "P1" : [15, 0, 0],     "P2" : [0, 0, 0],      "W" : 6,    "C": "00FF00"  },  // Front spar


That Object has 4 named fields: "P1", "P2", "W", and "C". The data is stored by name and can be accessed by name. It turns out that I didn't see the need for any Objects in this case, so all of the data were (and are) stored in simple Arrays. The original first line is:

[  15, 0, 0,     0, 0, 0,      6,    "00FF00" ],  // Front spar


That can be read as: a line segment starting at the 3D point (15, 0, 0) and ending at the 3D point (0,0,0) with a line width of 6 and a color of green. Every line segment is stored exactly like that.

Conclusion

JSON stands for "JavaScript Object Notation", and it is a widely accepted and widely used standard for sharing data. It's easily read and written by people without needing any special software. JSON isn't the most efficient way to store data, but it more than makes up for that inefficiency with portability.

P.S. There's a very enjoyable presentation by the creator of JSON at https://www.infoq.com/presentations/Heretical-Open-Source/.
Join a National Hang Gliding Organization: US Hawks at ushawks.org
View my rating at: US Hang Gliding Rating System
Every human at every point in history has an opportunity to choose courage over cowardice. Look around and you will find that opportunity in your own time.
Bob Kuczewski
User avatar
Contributor
Contributor
 
Posts: 8371
Joined: Fri Aug 13, 2010 2:40 pm
Location: San Diego, CA

Re: Simple (and Primitive) 3D Rendering Program

Postby Bob Kuczewski » Thu Oct 20, 2022 8:02 pm

Limitations

It's important to understand the limitations of any tool. The current version of this program was created as a "quick and dirty" way to share 3D design information (and the designs themselves). The format is very simple: just a list of point pairs that describe line segments and their color and width. There is no provision for surfaces (although they can be implied with various line styles such as parallel lines and cross hatching). This format is sufficient to allow proper drawing of these line segments with proper hidden line removal (front lines drawing over back lines).

However, the simple JavaScript program used to render this simple format does not currently account for line depth. It just draws the lines in the order that they were given. In some cases (such as a monochrome object) this is actually optimal since it doesn't matter which of two green lines were drawn on top of one another ... since the intersection is green in either case. Here's an example:

Monochrome_No_Errors.png
Monochrome_No_Errors.png (10.89 KiB) Viewed 2038 times

But when mutiple colors are used, the offending lines can be very confusing. Here's a recent example:

Visibile_Errors_Severe.png
Visibile_Errors_Severe.png (10.79 KiB) Viewed 2039 times

That is supposed to be a view of the Glider from the top. The blue kingpost is correct because it draws over top of the light blue cross bar. However, the red control bar should be drawn BEHIND the light blue cross bar and the yellow leading edge. This is done properly for the left (far) red downtube and red base tube. But the right red downtube is drawn on top of the light blue cross bar and the yellow leading edge.

I do know how to fix this. There are fairly complex algorithms to figure out which parts of which objects are behind other objects, and I can implement them. But that would take a considerable amount of coding and would also slow down the program (I'm not sure how much).

So while I also find this annoying, I don't have any immediate plans to fix it. The purpose of this program is to enable quick sharing of ideas and not for the production of final artwork. Furthermore, the data format described here does capture the correct relationships and it will render correctly in other viewing software when converted to a generic format. For example, the latest versions of this program can produce a ".obj" file which can be loaded into many other rendering programs (including Blender). I hope this explanation will eliminate any confusion about what's being seen on the screen. Also remember that motion can be a very big help in understanding 3D images. When something doesn't look right, move the object around and the motion will help your visual processing system to figure out what's what.
Join a National Hang Gliding Organization: US Hawks at ushawks.org
View my rating at: US Hang Gliding Rating System
Every human at every point in history has an opportunity to choose courage over cowardice. Look around and you will find that opportunity in your own time.
Bob Kuczewski
User avatar
Contributor
Contributor
 
Posts: 8371
Joined: Fri Aug 13, 2010 2:40 pm
Location: San Diego, CA

Next
Forum Statistics

Who is online

Users browsing this forum: No registered users and 28 guests

Options

Return to Technology Forum