5月的Plugin of the month:教你如何用F#在AutoCAD中生成神奇的螺环(Spiro)

这一个有点实验性的:我们的第一个有趣本月插件 (说它有趣是因为它不是为一个重要的与工作有关的目的而设计的:-)

我已经张贴过代码的 版本,但现在是想分享最新的。Scott 已经介绍了该插件,并宣布了它It’s Alive in the Lab

这是我们写的关于使用F#的第一个插件,这意味着一个额外的DLL需要和插件一起复制。除此之外,代码应该就像在VB.NETC#里编写的。

这里粘贴的是F#代码(其它文件,请从Autodesk Labs下载):

 

// Declare a specific namespace and module name

  

module Spiro.Commands

  

// Import managed assemblies

  

open Autodesk.AutoCAD.ApplicationServices

open Autodesk.AutoCAD.Runtime

open Autodesk.AutoCAD.DatabaseServices

open Autodesk.AutoCAD.EditorInput

open Autodesk.AutoCAD.Geometry

open Autodesk.AutoCAD.GraphicsInterface

open System

open DemandLoading

  

// Our global variables

  

let mutable _jigSegs = 1

let mutable _perfectSegs = 10

let mutable _innerOverOuter = 0.125

let mutable _aOverInner = 0.3333

  

// Return a sampling of points along a spiro's path

  

let pointsOnSpiro cenX cenY inRad outRad a tStart tEnd num =

  [|

    for i in tStart .. tEnd * num do

  

      let t = (float i) / (float num)

      let diff = inRad - outRad

      let ratio = inRad / outRad

      let x =

        diff * Math.Cos(ratio * t) +

          a * Math.Cos((1.0 - ratio) * t)

      let y =

        diff * Math.Sin(ratio * t) -

          a * Math.Sin((1.0 - ratio) * t)

  

      yield new Point2d(cenX + x, cenY + y)

  |]

  

// Different modes of acquisition for our jig

  

type AcquireMode =

  | Inner

  | Outer

  | A

  

type SpiroJig() as this = class

  inherit DrawJig()

  

  // Our member variables

  

  let mutable (_pl : Polyline) = null

  let mutable _cen = Point3d.Origin

  let mutable _norm = new Vector3d(0.0,0.0,1.0)

  let mutable _inner = 0.0

  let mutable _outer = 0.0

  let mutable _a = 0.0

  let mutable _mode = Outer

  

  member x.StartJig(ed : Editor, pt, pl) =

  

    // Set our center and start with the outer radius

  

    _cen <- pt

    _pl <- pl

    _mode <- Outer

    _norm <-

      ed.CurrentUserCoordinateSystem.CoordinateSystem3d.Zaxis

  

    let stat = ed.Drag(this)

    if stat.Status = PromptStatus.OK then

  

      // Next we get the inner radius

  

      _mode <- Inner

      let stat = ed.Drag(this)

      if stat.Status = PromptStatus.OK then

  

        // And finally the pen distance

  

        _mode <- A

        let stat = ed.Drag(this)

  

        if stat.Status = PromptStatus.OK then

          _innerOverOuter <- _inner / _outer

          _aOverInner <- _a / _inner

        stat

      else

        stat

    else

      stat 

  

  // Our Sampler function to acquire the various distances

  

  override x.Sampler prompts =

  

    // We're just acquiring distances

  

    let jo = new JigPromptDistanceOptions()

    jo.UseBasePoint <- true

    jo.Cursor <- CursorType.RubberBand

  

    // Local function to acquire a distance and return

    // the appropriate status

  

    let getDist (prompts : JigPrompts)

      (opts : JigPromptDistanceOptions) oldVal =

  

      let res = prompts.AcquireDistance(opts)

      if res.Status <> PromptStatus.OK then

        (SamplerStatus.Cancel, 0.0)

      else

        if oldVal = res.Value then

          (SamplerStatus.NoChange, 0.0)

        else

          (SamplerStatus.OK, res.Value)

  

    // Then we have slightly different behavior depending

    // on the info we're acquiring

  

    match _mode with

  

    // The outer radius...

  

    | Outer ->

      jo.BasePoint <- _cen

      jo.Message <- "/nRadius of outer circle: "

      let (stat, res) = getDist prompts jo _outer

      if stat = SamplerStatus.OK then

        _outer <- res

      stat

  

    // The inner radius...

  

    | Inner ->

      jo.BasePoint <-

        _cen + new Vector3d(_outer, 0.0, 0.0)

      jo.Message <- "/nRadius of smaller circle: "

      let (stat, res) = getDist prompts jo _inner

      if stat = SamplerStatus.OK then

        _inner <- res

      stat

  

    // The pen distance...

  

    | A ->

      jo.BasePoint <-

        _cen + new Vector3d(_outer - _inner, 0.0, 0.0)

      jo.Message <-

        "/nPen distance from center of smaller circle: "

      let (stat, res) = getDist prompts jo _a

      if stat = SamplerStatus.OK then

        _a <- res

      stat

  

  // Our WorldDraw function to display the Spiro and

  // the related temporary graphics

  

  override x.WorldDraw(draw : WorldDraw) =

  

    // Save our current colour, to reset later

  

    let col = draw.SubEntityTraits.Color

  

    // Make our construction geometry green

  

    draw.SubEntityTraits.Color <- (int16 3)

  

    match _mode with

  

    | Outer ->  // Draw the outer circle

  

      draw.Geometry.Circle(_cen, _outer, _norm)

        |> ignore

  

    | Inner ->  // Draw the outer and inner circles

  

      draw.Geometry.Circle(_cen, _outer, _norm)

        |> ignore

      draw.Geometry.Circle

        (_cen + new Vector3d(_outer - _inner, 0.0, 0.0),

        _inner, _norm)

          |> ignore

  

    | A ->  // Draw the outer and inner circles

  

      draw.Geometry.Circle(_cen, _outer, _norm)

        |> ignore

      draw.Geometry.Circle

        (_cen + new Vector3d(_outer - _inner, 0.0, 0.0),

        _inner, _norm)

          |> ignore

  

    // Check the RegenAbort flag...

    // If it's set then we drop out of the function

  

    if not draw.RegenAbort then

  

      draw.SubEntityTraits.Color <- col

  

      // If getting the outer radius fix the other

      // parameters relative to it (as the inner radius

      // comes later we only need to fix the pen distance

      // against it)

  

      if _mode = Outer then

        _inner <- _outer * _innerOverOuter

        _a <- _inner * _aOverInner

      else if _mode = Inner then

        _a <- _inner *_aOverInner

  

      // Generate the polyline with low accuracy

      // (fewer segments == quicker)

  

      if not draw.RegenAbort then

  

        // Generate our polyline

  

        x.Generate(_jigSegs)

  

        if not draw.RegenAbort then

  

          // And then draw it

  

          draw.Geometry.Polyline(_pl, 0, _pl.NumberOfVertices-1)

            |> ignore

  

    true

  

  // Generate a more accurate polyline

  

  member x.Perfect() =

  

    x.Generate(_perfectSegs)

  

  member x.Generate(num) =

  

    // Generate points based on the accuracy

  

    let pts =

      pointsOnSpiro

        _cen.X _cen.Y _inner _outer _a 0 300 num

  

    // Remove all existing vertices but the first

    // (we need at least one, it seems)

  

    while _pl.NumberOfVertices > 1 do

      _pl.RemoveVertexAt(0)

  

    // Add the new vertices to our polyline

  

    for i in 0 .. pts.Length-1 do

      _pl.AddVertexAt(i, pts.[i], 0.0, 0.0, 0.0)

  

    // Remove the first (original) vertex

  

    if _pl.NumberOfVertices > 1 then

      _pl.RemoveVertexAt(0)

  

end

  

// Our jig-based command

  

["ADNPLUGINS", "SPI", CommandFlags.Modal)>]

let spirojig() =

  

  // Let's get the usual helpful AutoCAD objects

  

  let doc =

    Application.DocumentManager.MdiActiveDocument

  let ed = doc.Editor

  let db = doc.Database

  

  // Prompt the user for the center of the spiro

  

  let cenRes = ed.GetPoint("/nSelect center point: ")

  

  if cenRes.Status = PromptStatus.OK then

  

    let cen = cenRes.Value

  

    // Create the polyline and run the jig

  

    let pl = new Polyline()

    let jig = new SpiroJig()

    let res = jig.StartJig(ed, cen, pl)

  

    if res.Status = PromptStatus.OK then

  

      // Perfect the polyline created, smoothing it up

  

      jig.Perfect()

  

      use tr =

        db.TransactionManager.StartTransaction()

  

      // Get appropriately-typed BlockTable and BTRs

  

      let bt =

        tr.GetObject

          (db.BlockTableId,OpenMode.ForRead)

          :?> BlockTable

  

      let ms =

        tr.GetObject

          (bt.[BlockTableRecord.ModelSpace],

          OpenMode.ForWrite)

          :?> BlockTableRecord

  

      // Add our polyline to the modelspace

  

      let id = ms.AppendEntity(pl)

      tr.AddNewlyCreatedDBObject(pl, true)

  

      tr.Commit()

  

["ADNPLUGINS", "SPISEGS", CommandFlags.Modal)>]

let spiroSegments() =

  

  // Let's get the usual helpful AutoCAD objects

  

  let doc =

    Application.DocumentManager.MdiActiveDocument

  let ed = doc.Editor

  let db = doc.Database

  

  // Prompt the user for the center of the spiro

  

  let pio =

    new PromptIntegerOptions(

      "/nEnter segment resolution for jigged spiro: ")

  pio.LowerLimit <- 1

  pio.UpperLimit <- 20

  pio.DefaultValue <- _jigSegs

  pio.UseDefaultValue <- true

  

  let segRes = ed.GetInteger(pio)

  

  if segRes.Status = PromptStatus.OK then

  

    _jigSegs <- segRes.Value

  

    pio.Message <-

      "/nEnter segment resolution for perfected spiro: "

    pio.DefaultValue <- _perfectSegs

  

    let segRes = ed.GetInteger(pio)

  

    if segRes.Status = PromptStatus.OK then

  

      _perfectSegs <- segRes.Value

  

["ADNPLUGINS", "REMOVESP", CommandFlags.Modal)>]

let removeSpiro() =

  

  try

    RegistryUpdate.UnregisterForDemandLoading()

  

    let doc =

      Application.DocumentManager.MdiActiveDocument

    doc.Editor.WriteMessage

      ("/nThe Spiro plugin will not be loaded" +

      " automatically in future editing sessions.")

  with _ -> () 

 

 

 

当您运行SPI命令你会看到当你拖拽螺环样的图形时出现的临时图形:

  

 

希望你(或你孩子)将一起享受此乐趣! :-)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值