Now that we’ve introduced the series and seen how to add some introductory text, theme music and a field of stars, it’s time for the Star Wars logo.
- The initial blue text
- The theme music
- The star field
- The disappearing Star Wars logo
- The crawling text
The logo came, once again, from the HTML opening crawl, which embeds some SVG content representing it. I copied this into a standalone SVG text file, ran it through a web-service to translate it to DXF and then opened that inside AutoCAD.
The outlines of the letters (or groups of letters) were continuous polylines – which was great – but as there were double lines I really wanted to combine these into a single polyline with width. I measured the distance between the edges – it was 0.0833 – and so I offset the inner edge outwards by half this amount, erased the originals and applied a width of 0.0833 to each of the remaining lines.
As you can see in the below GIF of the process:
You’ll notice that the logo only looks “right” when in a 3D visual style: one of the reasons we switch to a perspective view with “Realistic” for the star field onwards.
Now that we have the logo’s basic geometry, I wanted to capture it as code. I took the approach in this post as a basis, and modified the output code to be in F#. Once I knew it worked well, I went ahead and refactored the vertex definitions to be in a list format: a much cleaner approach for generating the polylines and adding them to the database, especially when working from a functional language that excels at this type of processing.
To simulate the movement of the logo, I decided to just scale it downwards – much simpler than moving it away. There’s definitely some display-dependent behaviour, here: on my retina screen I didn’t need a delay for the timing to work well, but I needed to add one when I changed resolution to record a video… and then needed to change again when I actually recorded (Camtasia seemed to be doing something to actually speed graphics throughput, interestingly). So this will definitely need tweaking on a per system basis.
I could also have included the logo in the supplied drawing as a block, of course, and then have scaled the block in the same way as I have the various polylines. That’s an avenue people may want to explore when fooling around with this code themselves.
Here’s the video of the updated crawl:
Here’s the updated F# code. The logo takes up a lot of space, but otherwise the additions are pretty modest.
module StarWars.Crawler
open Autodesk.AutoCAD.ApplicationServices
open Autodesk.AutoCAD.ApplicationServices.Core
open Autodesk.AutoCAD.DatabaseServices
open Autodesk.AutoCAD.Geometry
open Autodesk.AutoCAD.Runtime
open System
open System.Windows.Media
// The intro music MP3 file
let mp3 =
"http://s.cdpn.io/1202/Star_Wars_original_opening_crawl_1977.mp3"
// The layers we want to create as a list of (name, (r, g, b))
let layers =
[
("Stars", (255, 255, 255));
("Intro", (75, 213, 238));
("Crawl", (229, 177, 58))
]
// Create layers based on the provided names and colour values
// (only creates layers if they don't already exist... could be
// updated to make sure the layers are on/thawed and have the
// right colour values)
let createLayers (tr:Transaction) (db:Database) =
let lt =
tr.GetObject(db.LayerTableId, OpenMode.ForWrite) :?> LayerTable
layers |>
List.iter (fun (name, (r, g, b)) ->
if not(lt.Has(name)) then
let lay = new LayerTableRecord()
lay.Color <-
Autodesk.AutoCAD.Colors.Color.FromRgb(byte r, byte g, byte b)
lay.Name <- name
lt.Add(lay) |> ignore
tr.AddNewlyCreatedDBObject(lay, true)
)
// Get a view by name
let getView (tr:Transaction) (db:Database) (name:string) =
let vt =
tr.GetObject(db.ViewTableId, OpenMode.ForRead) :?> ViewTable
if vt.Has(name) then
tr.GetObject(vt.[name], OpenMode.ForRead) :?> ViewTableRecord
else
null
// Add an entity to a block and a transaction
let addToDatabase (tr:Transaction) (btr:BlockTableRecord) o =
btr.AppendEntity(o) |> ignore
tr.AddNewlyCreatedDBObject(o, true)
// Flush the graphics for a particular document
let refresh (doc:Document) =
doc.TransactionManager.QueueForGraphicsFlush()
doc.TransactionManager.FlushGraphics()
// Transform between the Display and World Coordinate Systems
let dcs2wcs (vtr:AbstractViewTableRecord) =
Matrix3d.Rotation(-vtr.ViewTwist, vtr.ViewDirection, vtr.Target) *
Matrix3d.Displacement(vtr.Target - Point3d.Origin) *
Matrix3d.PlaneToWorld(vtr.ViewDirection)
// Poll until a music file has downloaded fully
// (could sleep or use a callback to avoid this being too
// CPU-intensive, but hey)
let rec waitForComplete (mp:MediaPlayer) =
if mp.DownloadProgress < 1. then
System.Windows.Forms.Application.DoEvents()
waitForComplete mp
// Poll until a specified delay has elapsed since start
// (could sleep or use a callback to avoid this being too
// CPU-intensive, but hey)
let rec waitForElapsed (start:DateTime) delay =
let elapsed = DateTime.Now - start
if elapsed.Seconds < delay then
System.Windows.Forms.Application.DoEvents()
waitForElapsed start delay
// Create the intro text as an MText object relative to the view
// (has a parameter to the function doesn't execute when loaded...
// also has hardcoded values that make it view-specific)
let createIntro _ =
let mt = new MText()
mt.Contents <-
"{\\fFranklin Gothic Book|b0|i0|c0|p34;" +
"A long time ago, in a galaxy far,\\Pfar away...}"
mt.Layer <- "Intro"
mt.TextHeight <- 0.5
mt.Width <- 10.
mt.Normal <- Vector3d.ZAxis
mt.TransformBy(Matrix3d.Displacement(new Vector3d(1., 6., 0.)))
mt
// Generate a quantity of randomly located stars... a list of (x,y)
// tuples where x and y are between 0 and 1. These will later
// get transformed into the relevant space (on the screen, etc.)
let locateStars quantity =
// Create our random number generator
let ran = new System.Random()
// Note: _ is only used to make sure this function gets
// executed when it is called... if we have no argument
// it's a value that doesn't require repeated execution
let randomPoint _ =
// Get random values between 0 and 1 for our x and y coordinates
(ran.NextDouble(), ran.NextDouble())
// Local recursive function to create n stars at random
// locations (in the plane of the screen)
let rec randomStars n =
match n with
| 0 -> []
| _ -> (randomPoint 0.) :: randomStars (n-1)
// Create the specified number of stars at random locations
randomStars quantity
// Take locations from 0-1 in X and Y and place them
// relative to the screen
let putOnScreen wid hgt dcs (x, y) =
// We want to populate a space that's 2 screens high (so we
// can pan/rotate downwards at the end of the crawl), hence
// the additional multiplier on y
let pt = new Point3d(wid * (x - 0.5), hgt * ((y * -1.5) + 0.5), 0.)
pt.TransformBy(dcs)
// Create a polyline from a vertex list
let polyFromVerts n w m lay verts =
let pl = new Polyline(List.length verts)
pl.Normal <- n
List.iteri
(fun i v ->
pl.AddVertexAt(i, new Point2d(fst v, snd v), 0., w, w)
)
verts
pl.Closed <- true
pl.TransformBy(m)
pl.Layer <- lay
pl
// Create the Star Wars logo from a list of vertex lists
let createLogo n w (p:Point3d) =
let verts =
[
[
(3.44258, 3.55679214868466);
(3.44258, 3.17553);
(2.8459, 3.17553);
(2.8459, 2.26434);
(2.40686, 2.26434);
(2.40686, 3.17612262061896);
(1.75655462335279, 3.17429248415801);
(1.72128333960472, 3.16669182023216);
(1.70763551483587, 3.15445862216978);
(1.70053119527792, 3.13782570874412);
(1.69872472722736, 3.12280350074472);
(1.69905468222644, 3.1191373340882);
(1.70087251722259, 3.11283393221843);
(1.70607593564196, 3.10180962306061);
(1.71621799674036, 3.08484068174517);
(1.73226620616926, 3.06151479594735);
(1.75494342528422, 3.03081841447927);
(1.78484748611009, 2.99191745820677);
(1.82294826291299, 2.94341738705363);
(1.87617804512918, 2.87460815638392);
(1.92246977710667, 2.81233246534401);
(1.95733932573181, 2.76278154528292);
(1.97888966827266, 2.72730122581048);
(1.99798396461003, 2.61953152517619);
(1.97570333908842, 2.50537461239132);
(1.91940141039899, 2.40109844384521);
(1.83646768287547, 2.32393324739609);
(1.80352751564867, 2.30404166777099);
(1.77133382731068, 2.28892985436458);
(1.73039688525943, 2.27813889539216);
(1.6754267050528, 2.27134897179126);
(1.59812878352077, 2.26745012799826);
(1.48927961179153, 2.26536230567172);
(1.33992701030606, 2.26450009692313);
(1.14136357854396, 2.26434001353564);
(1.04351, 2.26434);
(0.353207, 2.26434);
(0.353207, 2.68233);
(1.40159102170787, 2.68233);
(1.41748209051223, 2.69338144406387);
(1.43267712171974, 2.70939240011938);
(1.44055338604423, 2.73157466234716);
(1.44119451950013, 2.7367666633397);
(1.44105860097375, 2.73978455802721);
(1.43997063735183, 2.74322220229697);
(1.43439829934938, 2.75301581981413);
(1.4213959120805, 2.77071589918082);
(1.39979463633981, 2.7968603003749);
(1.36874714824015, 2.83262747962015);
(1.32697107584659, 2.87983734880692);
(1.23683919774444, 2.98529715322583);
(1.18339822046774, 3.06424778707422);
(1.16026089514802, 3.13669377471704);
(1.15987518361763, 3.21768150787052);
(1.18779743410553, 3.32756187144587);
(1.25599700588018, 3.43654449583423);
(1.36395601807001, 3.52111523123897);
(1.50483659505006, 3.55390286925382)
];
[
(4.39994870002626, 3.54702);
(4.84743214991296, 2.25278501118727);
(4.41662097329373, 2.25517027344357);
(4.36118115868732, 2.43645696748207);
(3.81392247714979, 2.4388933038331);
(3.75959503424206, 2.26439);
(3.32291291955518, 2.26439);
(3.76562469596367, 3.54702)
];
[
(3.93947639266304, 2.78028246119739);
(4.0036225829752, 2.96145971580626);
(4.09333768797878, 3.21205266525835);
(4.17869162137176, 2.9611015523335);
(4.23989035006252, 2.78062648416092)
];
[
(5.54586280271403, 3.54702);
(5.73207467832675, 3.54636901457996);
(5.88112842531212, 3.54435138355133);
(5.98711334518623, 3.54111056421452);
(6.04631520398901, 3.53650379705603);
(6.11453578628832, 3.51811946058964);
(6.17810911285892, 3.48782673131194);
(6.23583237180861, 3.44644129584782);
(6.28596484458466, 3.39522327740142);
(6.32595493380338, 3.3422331096743);
(6.35270689747694, 3.29157143345469);
(6.3680867123442, 3.23557047460095);
(6.37643733628869, 3.16568660578167);
(6.36992931993028, 3.05606170860937);
(6.33210762885031, 2.95400067046692);
(6.26513711406835, 2.86555263333169);
(6.17366080953793, 2.79497351541438);
(6.14309800665153, 2.7769730033941);
(6.11700705528388, 2.76203194437172);
(6.1127843266014, 2.7596756036369);
(6.13885495853737, 2.73241774032095);
(6.17040343063679, 2.70456002005355);
(6.19335603450253, 2.68673568368009);
(6.86633739559967, 2.67707780533635);
(6.8742792314147, 2.26439);
(6.43526528939969, 2.26439);
(6.2418481178589, 2.26499101663952);
(6.11292604672176, 2.26701660378081);
(6.03406510582276, 2.27102207373412);
(5.9876655077232, 2.2786416155789);
(5.93407000168671, 2.30917788766821);
(5.85931623340334, 2.36822655452807);
(5.75524877978626, 2.46058692597491);
(5.61622131796047, 2.59075075972907);
(5.53661647932702, 2.66649674082322);
(5.5380039627306, 2.26439);
(5.0687, 2.26439);
(5.0687, 3.54702)
];
[
(5.53061, 3.22193);
(5.67562434528338, 3.22193);
(5.76549694080276, 3.22143652653738);
(5.8227611211413, 3.21969654059112);
(5.85910049504742, 3.21603930873005);
(5.8835168373915, 3.20907338608929);
(5.91186718137154, 3.19493342317029);
(5.93449705925347, 3.17520079608985);
(5.9461460801979, 3.14851545023844);
(5.94993368385409, 3.1219164675812);
(5.95063789889132, 3.09471968284322);
(5.94382358152697, 3.06635439430217);
(5.92659250539508, 3.04172336065536);
(5.90396433067426, 3.02224480067035);
(5.89094323070014, 3.01257677365093);
(5.85401438673261, 3.00939777579999);
(5.81195751173521, 3.00827403940718);
(5.75772390976083, 3.00788023741928);
(5.69388269742043, 3.00778);
(5.53061, 3.00778)
];
[
(1.57896862502186, 2.10589);
(1.67880828853815, 1.81260822369826);
(1.71379594628957, 1.71227377415791);
(1.73527827819659, 1.65374851057995);
(1.75920847744905, 1.72143678846549);
(1.79238473443157, 1.81629031452661);
(1.82529369636899, 1.9108498644361);
(1.85381147382987, 1.99319569627039);
(1.87388905179331, 2.05149770143778);
(1.8912822178088, 2.10154);
(2.32183990237932, 2.10154);
(1.87988984918951, 0.813874);
(1.58239936659396, 0.813874);
(1.56171424001044, 0.873466415317032);
(1.53295553769481, 0.956661589981072);
(1.49226696124087, 1.07432847254541);
(1.44570537030483, 1.20935203857834);
(1.39914465363298, 1.34447313546997);
(1.35858792560323, 1.46260098506068);
(1.35731504835852, 1.46634287831389);
(1.12278635463374, 0.823096);
(0.828087008371704, 0.823096);
(0.391580354628849, 2.10592925972778);
(0.820152580844014, 2.10564013321748);
(0.837989023926543, 2.05516759780397);
(0.859154236649471, 1.99519000485767);
(0.889137620919735, 1.91045548589063);
(0.92363489102879, 1.81330725414169);
(0.95817174136269, 1.71621235357281);
(0.979127616594254, 1.65771496109079);
(1.13684321996318, 2.10589)
];
[
(3.28904785776228, 2.11550813590997);
(3.73761724891686, 0.824937551963215);
(3.30662126941653, 0.822954990623427);
(3.24971008910397, 1.00471601224713);
(2.69993521628902, 1.00914873793685);
(2.64593333868056, 0.827492143980233);
(2.21818710344123, 0.831947254825196);
(2.6611172170532, 2.11491182100224)
];
[
(2.83075227905127, 1.35366041729816);
(2.87923452796852, 1.48620742282014);
(2.91710514665256, 1.58972911414478);
(2.98271654250203, 1.76897755710787);
(3.04320129635346, 1.59028356566733);
(3.05209063261682, 1.56402552642664);
(3.06700400017455, 1.52004510187737);
(3.0854341923654, 1.46582399091646);
(3.12362427253239, 1.35375);
(2.96565, 1.35375)
];
[
(6.86738701658834, 2.11055);
(6.86636282954738, 1.71143316113769);
(6.80968306747308, 1.71166033453278);
(6.76879036498295, 1.71182022759786);
(6.70799358315783, 1.71199016269997);
(6.63307325593112, 1.7122001636159);
(6.55001968254444, 1.71242014609799);
(6.46487686643307, 1.71259008296755);
(6.38353162001043, 1.71274);
(6.3119, 1.71274);
(6.27257783369629, 1.71274);
(6.24205274248879, 1.71264056973548);
(6.22660212251127, 1.7125107967416);
(6.2251332275232, 1.71152567832907);
(6.21826693670348, 1.69895889104225);
(6.21368777582741, 1.67971131922799);
(6.21435024322066, 1.66802861722527);
(6.21739642079077, 1.6617633178101);
(6.23123505854596, 1.64006984102296);
(6.25133503104544, 1.61172470022028);
(6.27506976405328, 1.58087731010094);
(6.30343445446908, 1.54536194073766);
(6.33613016667173, 1.50446982038211);
(6.36882765268814, 1.46364795338937);
(6.39759648916047, 1.42768942522637);
(6.42698279823146, 1.39058529555372);
(6.45061281453046, 1.35962111552111);
(6.46946283661554, 1.33294897542795);
(6.48427168722091, 1.30886051606526);
(6.49558070778541, 1.2857968585775);
(6.50373858922054, 1.26288773947885);
(6.50912773260329, 1.23975035055556);
(6.51327721344814, 1.21302571797147);
(6.51175336049161, 1.12736501317236);
(6.48652470110405, 1.04165809320606);
(6.44008974336086, 0.963007688635264);
(6.37622624858673, 0.897126531506992);
(6.34136126462528, 0.869847315902196);
(6.30454078761051, 0.848196296706673);
(6.25735068769598, 0.832813348711309);
(6.1946926284406, 0.82334703105736);
(6.10791480816548, 0.817863341131013);
(5.98694154482626, 0.815070019097485);
(5.82230156071494, 0.814036130538472);
(5.60480157525874, 0.813819020750331);
(5.57579464995636, 0.813819);
(5.31033615083933, 0.814524554163534);
(5.1147019020476, 0.816483302483575);
(4.987492484323, 0.819719154126808);
(4.92211642121057, 0.824809172133531);
(4.86255265351012, 0.851353630113647);
(4.78599624454765, 0.907412194416292);
(4.67949985067332, 1.00068616876809);
(4.53106852156296, 1.14052267830742);
(4.47120745637841, 1.19730089863496);
(4.43722713582279, 1.22872889310876);
(4.43479568627557, 0.813638441379537);
(3.97790059619618, 0.815626482435268);
(3.9754088950639, 2.09644);
(4.85435350955452, 2.09644);
(4.9524384120744, 2.08116590914619);
(5.0421132756076, 2.04495127286902);
(5.11968748422517, 1.9926567681356);
(5.18396561975463, 1.92855365816238);
(5.23369876113581, 1.8565584931861);
(5.26760265295347, 1.78012486674058);
(5.28395979085181, 1.70241419345942);
(5.28004510702221, 1.62839637688364);
(5.25918823660479, 1.55956085936412);
(5.23129767989095, 1.49769758838801);
(5.19287713035147, 1.44636773420326);
(5.14201539837177, 1.39663698792505);
(5.08439692602106, 1.35247865701708);
(5.02763448373853, 1.32012290369814);
(5.01926153493527, 1.31567062139799);
(5.0201401696421, 1.31465755077974);
(5.03635825437325, 1.29789855762624);
(5.05404454395319, 1.28126513332677);
(5.07123638095395, 1.26662725426366);
(5.08545995358084, 1.25609066762876);
(5.09155854789262, 1.25261446887105);
(5.11520296200932, 1.24773262641588);
(5.1839384665362, 1.2437210842384);
(5.31499237416825, 1.24167909762829);
(5.52681922465014, 1.24108996855417);
(5.63722185856806, 1.24103002684722);
(5.75066647981753, 1.24129939671563);
(5.83424919549954, 1.24231918109318);
(5.89196733024185, 1.24477834974135);
(5.92703259711752, 1.248815584514);
(5.94186413459469, 1.25312246895092);
(5.94452720971008, 1.2550352046588);
(5.94806585844389, 1.26045738811548);
(5.95485974741274, 1.27515168783283);
(5.95764005574495, 1.28273863090887);
(5.95785068079404, 1.28531001171654);
(5.95662977011935, 1.29060599364051);
(5.95018212309273, 1.30349479459504);
(5.9355344205503, 1.32495058423466);
(5.9118829225398, 1.3549665197346);
(5.87849452603907, 1.39425562054069);
(5.83444545149862, 1.44431638015785);
(5.73724255457614, 1.5576766597549);
(5.68852920511552, 1.65032448217977);
(5.6739520592985, 1.72315250284916);
(5.67339629718887, 1.7776616505618);
(5.67582917215106, 1.81532530917471);
(5.6869349467792, 1.86440938045866);
(5.70875633588307, 1.91894581413262);
(5.74384006043308, 1.97499767954953);
(5.79476342169442, 2.02753710966927);
(5.86330983842433, 2.07103004485607);
(5.95041130228872, 2.10023544319704);
(6.0548074435266, 2.11055)
];
[
(4.4345937394936, 1.56212643206149);
(4.43518042620646, 1.67044148437216);
(4.43556787492364, 1.77298062965549);
(4.59383378518243, 1.77217924888779);
(4.64881620553342, 1.77178759329077);
(4.70616920376935, 1.77085699790493);
(4.75610194813188, 1.76893449843586);
(4.79998632985362, 1.76368084421198);
(4.84719213337829, 1.71746157785606);
(4.8610886482744, 1.66244080023347);
(4.85668777505293, 1.63727739146715);
(4.84628131477829, 1.61325711967745);
(4.82603724633321, 1.58950045930602);
(4.79564620691826, 1.57081593809471);
(4.76918190987008, 1.56332632934342);
(4.73276899886416, 1.5595967363527);
(4.67535969661275, 1.55779395371568);
(4.58510256995686, 1.55724810481441);
(4.52224299866422, 1.5581940498193)
]
]
// Displace our vertices to the specified point and then
// apply a hardcoded, view-specific scaling
let m1 = Matrix3d.Displacement(p.GetAsVector())
let m = m1 * Matrix3d.Scaling(1.4, p + new Vector3d(3.5,0.,0.))
verts |> List.map (polyFromVerts n w m "Crawl")
// Commands to recreate the open crawl experience for a selected
// Star Wars episode
[<CommandMethod("EPISODE")>]
let episode() =
// Make sure the active document is valid before continuing
let doc = Application.DocumentManager.MdiActiveDocument
if doc <> null then
let db = doc.Database
let ed = doc.Editor
// Start our transaction and create the required layers
use tr = doc.TransactionManager.StartTransaction()
createLayers tr db
// Get our special Initial and Crawl views
let ivtr = getView tr db "Initial"
let cvtr = getView tr db "Crawl"
if ivtr = null || cvtr = null then
ed.WriteMessage(
"\nPlease load StarWarsCrawl.dwg before running command.")
doc.TransactionManager.EnableGraphicsFlush(true)
let btr =
tr.GetObject(doc.Database.CurrentSpaceId, OpenMode.ForWrite)
:?> BlockTableRecord
// Set the initial view: this gives us higher quality text
ed.SetCurrentView(ivtr)
// First we create the intro text
let intro = createIntro ()
intro |> addToDatabase tr btr
// Make sure the intro text is visible
doc |> refresh
ed.UpdateScreen()
// We'll now perform a number of start-up tasks, while our
// initial intro text is visible... we'll start vy recording
// our start time, so we can synchronise our delay
let start = DateTime.Now
// Get our view's DCS matrix
let dcs = dcs2wcs(cvtr)
// Create a host of stars at random screen positions
locateStars 1000 |>
List.iter
(fun xy ->
let p = putOnScreen cvtr.Width cvtr.Height dcs xy
let dbp = new DBPoint(p)
dbp.Layer <- "Stars"
dbp |> addToDatabase tr btr)
// Open the intro music over the web
let mp = new MediaPlayer()
mp.Open(new Uri(mp3))
// Wait for the download to complete before playing it
waitForComplete mp
// Have a minimum delay of 5 seconds showing the intro text
waitForElapsed start 5
// Start the audio at 8.5 seconds in
mp.Position <- new TimeSpan(0, 0, 0, 8, 500)
mp.Play()
// Switch to the crawl view: this will also change the
// visual style from 2D Wireframe to Realistic
ed.SetCurrentView(cvtr)
// Remove the intro text
intro.Erase()
// Draw the SW logo and "move it away" from the camera,
// once again with hardcoded, view-specific values
let es =
createLogo cvtr.ViewDirection 0.0833
(new Point3d(-1.,-5.,0.))
es |> List.iter (addToDatabase tr btr)
// Use a simple scaling to give the impression of movement
// (yes, with hardcoded, view-specific values)
let rec moveAway (es:Polyline list) n =
let m = Matrix3d.Scaling(0.91, new Point3d(3.,12.,0.))
es |>
List.iter
(fun e ->
e.TransformBy(m)
refresh doc
System.Threading.Thread.Sleep(15))
if n > 0 &&
not(HostApplicationServices.Current.UserBreak()) then
moveAway es (n-1)
moveAway es 40
// Remove the polylines making up the logo
es |> List.iter (fun e -> e.Erase())
tr.Commit() // Commit the transaction
In tomorrow’s post we’ll wrap up the series by adding the crawl text itself.