Saturday, May 17, 2014

Sketchup Plugin #1


Here's the ruby that I wrote for a plugin for Sketchup.


require 'sketchup' def do_curve(x) a1 = 0 a2 = 0 a3 = 4.7173806e-006 a4 = -1.13578e-007 a5 = 8.4145793e-010 r = -1.111234 c = 1.0/r k = -3.739907 return (c * x * x) / (1 + (1 - (1 + k) * c * c * x * x) ** 0.5) end def draw_curve model = Sketchup.active_model entities = model.entities model.start_operation "DrawCurve" last_num = 0 last_y = 0 for i in -1000 .. 1000 floati = i.to_f / 100 y = do_curve(floati) if i != -1000 Sketchup.active_model.entities.add_line [floati, y, 0], [last_num, last_y, 0] end last_num = floati last_y = y end model.commit_operation end UI.menu("PlugIns").add_item("Draw Curve") { draw_curve } #draw_curve
I used this to draw a curve in sketchup, which I turned into a 3d shape to print out on my 3d printer.

This represents some interesting skills that I've acquired.
1) The ability to program in Ruby, or rather the skill to write a short program in an unknown language in about an hour.
2) The ability to programmatically control a 3d modelling program, in this case sketchup.
3) The ability to turn that 3d model into a physical object, using a 3d printer.


Tuesday, March 25, 2014

Dilbert Me in F#

This is the Dilbert download tool in F#, just because I like playing with F# and making up reasons to do so.

I switched to WebClient here instead of WebRequest. It makes things a little easier and I clearly should have been using it all along.

I wrote it in Visual Studio 2013 as a F# script file. I selected all then hit 'alt+enter' to run it.

FsLight was what I was led to when googling F# code formatters. It was easy to use and seems to have done a good job.

1
2
3
4
5
6
7
8
let webClient = new System.Net.WebClient()
let url = "http://www.dilbert.com"
let dilbert = webClient.DownloadString(url)
let innerUrl = "http://www.dilbert.com/" + System.Text.RegularExpressions.Regex.Match(dilbert, "src=.*strip.gif").Value.Substring(5)
innerUrl
let innerTempFile = System.IO.Path.GetTempFileName() + ".gif"
webClient.DownloadFile(innerUrl, innerTempFile)
System.Diagnostics.Process.Start(innerTempFile)
Created with FsLight

Monday, March 17, 2014

Dilbert Me 2

The previous powershell script for dilbert broke. It seems the cartoons aren't sequential. This is try #2. 

$url = "http://www.dilbert.com" 
$httpRequest = [Net.WebRequest]::create($url)

$response = $httpRequest.GetResponse()
$stream = $response.GetResponseStream()
$buffer = @()
$binaryReader = New-Object System.IO.BinaryReader($stream)

$tempBuffer = $binaryReader.ReadBytes(1024)
while($tempBuffer.Length -eq 1024) { 
    $buffer += $tempBuffer
    $tempBuffer = $binaryReader.ReadBytes(1024)
}

$dilbert = [System.Text.ASCIIEncoding]::UTF8.GetString($buffer)

$url = "http://www.dilbert.com/" + [System.Text.RegularExpressions.Regex]::Match($dilbert, "src=.*strip.gif").Value.Substring(5)

$url 

$httpRequest = [Net.WebRequest]::create($url)

$response = $httpRequest.GetResponse()
$stream = $response.GetResponseStream()
$buffer = @()
$binaryReader = New-Object System.IO.BinaryReader($stream)

$tempBuffer = $binaryReader.ReadBytes(1024)
while($tempBuffer.Length -eq 1024) { 
    $buffer += $tempBuffer
    $tempBuffer = $binaryReader.ReadBytes(1024)
}

$buffer += $tempBuffer
$tempFile = [System.IO.Path]::GetTempFileName() + ".gif"
[System.IO.File]::WriteAllBytes($tempFile, $buffer)
.$tempFile

Tuesday, March 11, 2014

Dilbert Me!

My RSS reader (feedly) doesn't handle Dilbert well. Probably because they want me to view all the advertising surrounding the daily Dilbert cartoon. So it opens up an external browser and forwards me to Dilbert.com from the RSS reader. This is annoying. This powershell script opens up today's Dilbert and shows just the strip. I should wrap this in it's own RSS feed... eventually

$startDate = New-Object DateTime(1425, 9, 14)
$days = [DateTime]::Now.Subtract($startDate).Days
$hundredThousands = [Math]::Floor( $days / 100000)
$tenThousands = [Math]::Floor( ($days % 100000)/10000)
$thousands = [Math]::Floor( ($days % 10000)/1000)
$hundreds = [Math]::Floor( ($days %1000) /100)
$remainder = [Math]::Floor( ($days %100))
#http://dilbert.com/dyn/str_strip/000000000/00000000/0000000/200000/10000/4000/900/214940/214940.strip.gif
$url = "http://dilbert.com/dyn/str_strip/000000000/00000000/0000000/" + $hundredThousands + "00000/" + $tenThousands + "0000/" + $thousands + "000/" + $hundreds + "00/" + $days + "/" + $days + ".strip.gif"

$httpRequest = [Net.WebRequest]::create($url)

$response = $httpRequest.GetResponse()
$stream = $response.GetResponseStream()
$buffer = @()
$binaryReader = New-Object System.IO.BinaryReader($stream)

$tempBuffer = $binaryReader.ReadBytes(1024)
while($tempBuffer.Length -eq 1024) { 
 $buffer += $tempBuffer
 $tempBuffer = $binaryReader.ReadBytes(1024)
}

$buffer += $tempBuffer
$tempFile = [System.IO.Path]::GetTempFileName() + ".gif"
[System.IO.File]::WriteAllBytes($tempFile, $buffer)
.$tempFile

Wednesday, March 5, 2014

Drawing in a canvas.

I created an html 5 canvas object.
Mouse down to draw in it.

Sadly, this only works if it is the only post viewed.
JSFiddle was very useful in debugging this and getting it to work. I did the development on a PC, and tested the results immediately in a browser on a tablet and on my phone.



Monday, June 17, 2013

How to write a loop, or lambda expression, in the immediate window, part 2

$dte is the key.

That whole nuget package manager console window, that is a powershell window, that gives you easy access to the visual studio automation model, is neat, very powerful, and, as I have discovered, a treasure trove of Visual Studio arcana.

EnvDTE.DTE is easy to get at when you are in the context of an add-in that you have written yourself. Exposing it in ready made powershell window, is a complex and involved trick. Writing that whole powershell console as an add-in inside of Visual Studio, after reading the source code at nuget.codeplex.com, appears to be an elegant work of coding art.

But.. back to $dte, my favorite powershell-nuget-visualstudio variable. Here is the code that worked the magic and populated the variable inside of powershell:

 
        [System.Diagnostics.CodeAnalysis.SuppressMessage(
           
"Microsoft.Reliability",
           
"CA2000:Dispose objects before losing scope",
            Justification =
"We can't dispose it if we want to return it.")]
       
private static Tuple CreateRunspace(IConsole console, string hostName)
        {
            DTE dte = ServiceLocator.GetInstance();
InitialSessionState initialSessionState = InitialSessionState.CreateDefault();
            initialSessionState.Variables.Add(
               
new SessionStateVariableEntry(
                   
"DTE",
                    (DTE2)dte,
                   
"Visual Studio DTE automation object",
                    ScopedItemOptions.AllScope | ScopedItemOptions.Constant)
            );
// this is used by the functional tests
           
var packageManagerFactory = ServiceLocator.GetInstance();
           
var pmfTuple = Tuple.Create<string, object>("packageManagerFactory", packageManagerFactory);
Tuple<string, object>[] privateData = new Tuple<string, object>[] { pmfTuple };
var host = new NuGetPSHost(hostName, privateData)
            {
                ActiveConsole = console
            };
var runspace = RunspaceFactory.CreateRunspace(host, initialSessionState);
            runspace.ThreadOptions = PSThreadOptions.Default;
            runspace.Open();
//
           
// Set this runspace as DefaultRunspace so I can script DTE events.
           
//
           
// WARNING: MSDN says this is unsafe. The runspace must not be shared across
           
// threads. I need this to be able to use ScriptBlock for DTE events. The
           
// ScriptBlock event handlers execute on DefaultRunspace.
           
//
            Runspace.DefaultRunspace = runspace;
return Tuple.Create(new RunspaceDispatcher(runspace), host);
        }
 
 
 
So here is what I learned from strolling down this rabbit hole.
  1. They brute forced the whole console window. They didn't recycle any sort of existing powershell or command window. They wrote a new one and dealt with all the keypress by keypress nastiness that is involved therein.
  2. $dte is actually spelled $DTE, and just a variable passed into a Runspace through its InitialSessionState.
  3. OpenSource code is fun.

Also, and unrelated to codeplex and nuget, if you want to create a headless $dte object, or rather one connected to a new instance of visual studio 2012, that is simply:

$dte = New-Object -comobject VisualStudio.DTE.11.0

Getting the running obect is done like this:

$dte = [System.Runtime.InteropSrevices.Marshal]::GetActiveObject("VisualStudio.DTE.11.0")

Wednesday, June 12, 2013

How to write a loop, or lambda expression, in the immediate window

First, you can't write lambda expressions in the immediate window. There are some very good, boring reasons why this is the case. Jared Par explains here...

However, what you really wanted to do is write a loop inside the immediate window because there is some particular debugging goal that you are trying to achieve. This post is about how you can do that.

For example, let's say we have an array of a large number of elements, 1000 or so should do. Let's also say that there is one piece of data in that array that is relevant. We wish to inspect each element individually and test the validity of each item. The following code snipped creates this situation.

static void Main(string[] args)
{ 
  List<int> ints = new List<int>();
  for (int i = 0; i < 1000; i++)
  {
    ints.Add(i);
  }
 
 
  Random random = new Random();
  ints[random.Next(1000)] = -12;
  Console.ReadLine();
}

In this artificial case we're looking for the index of the element in the array that is equal to -12.

The immediate window forces us to do this one item in the array at a time. Potentially taking 1000 user-interactive steps.

Since that method sucks, here's an alternative:
  1. Run the program in the debugger and set a breakpoint on the ReadLine() statement.
  2. Open up the nuget package manager console.
  3. Write some clever powershell that does the evaluation and looping for us.
The clever bit of powershell code that is necessary starts with something like this:

$dte.Debugger.GetExpression("expression", 1, $true)

$dte is the variable that contains the Visual Studio automation model.
Documentation about $dte is here.

In our case, the completed powershell expression looks like this:

for($i = 0; $i -lt 1000; $i++) { $a = $dte.Debugger.GetExpression("ints[$i]"); if ($a.Value -ne $i) { Write-Host $a.Value $i } }

Or, broken into multiple, more readable lines:

for($i = 0; $i -lt 1000; $i++) {
  $a = $dte.Debugger.GetExpression("ints[$i]");
  if ($a.Value -ne $i) {
    Write-Host $a.Value $i
  }
}

This rather neatly writes out the index we're looking for and the value found there. 



















The anomalous value (-12) wound up in the 604th element of the array. Groovy.

Most programmers aren't going to do this. It requires good knowledge of powershell, some knowledge of the Visual Studio automation model, and an interesting and difficult debugging problem before it is useful.

But it is fun.