diff options
author | Dominic Clifton <me@dominicclifton.name> | 2015-02-02 03:06:45 +0300 |
---|---|---|
committer | Dominic Clifton <me@dominicclifton.name> | 2015-02-02 03:07:44 +0300 |
commit | e871466869a9adf0200f3484d4ea7c95a2b51d51 (patch) | |
tree | ad62f0b8c1d8b22ad64b14e310895e3b83ab146a /support/svg_model_motors.rb | |
parent | bd2754a0de2080a1bf11d12d0923aac8e47b665f (diff) |
Moving script.
Diffstat (limited to 'support/svg_model_motors.rb')
-rwxr-xr-x | support/svg_model_motors.rb | 546 |
1 files changed, 546 insertions, 0 deletions
diff --git a/support/svg_model_motors.rb b/support/svg_model_motors.rb new file mode 100755 index 00000000..97ee4f17 --- /dev/null +++ b/support/svg_model_motors.rb @@ -0,0 +1,546 @@ +#!/usr/bin/ruby + +# Simple SVG model generator for MW compatible FCs +# (c) 2015 Jonathan Hudson +# Licence MIT or GPL v2 or later (as you wish) + +require 'cairo' +require 'stringio' +include Math + +TFLAT=true # Whether to make Tri / V-tails flat or veed +YFLAT=false # Whether to make Y{4,6} flat or veed + +# Calculate motor coordinates for non-trivial platforms +class ShapeFarm + RAD=0.017453292 + def generate xp,yp,radius,ns=6,offset=0 + p=[] + 0.upto(ns-1) do |n| + ang = n*360.0/ns + offset + ang %= 360 + x = radius*sin(RAD*ang) + y = radius*cos(RAD*ang) + p << [xp+x, yp-y] # y axis inverted for cairo coord set + end + p + end +end + +# The SVG generator +class Model + attr_accessor :lw, :radius + + RAD = 0.017453292 + RADIUS=28 + + # Motor direction for arrow indicators + CW=0 + CCW=1 + NOARROW=-1 + + # Position on the motor circle for arrow indicators + NE=0 + SE=1 + SW=2 + NW=3 + + # We use StringIO in order to be able to add a (non-)copyright statement + def initialize filename + @lw = RADIUS + @radius = RADIUS + @name = filename + @output = StringIO.new + @surface = Cairo::SVGSurface.new(@output, 200,200) + @cr = Cairo::Context.new(@surface) + end + + # Draw an arbitrary path, x,y pairs terminated by ! + def draw_path path,fill=:silver,round=false + @cr.set_line_cap(Cairo::LINE_JOIN_ROUND) if round + @cr.set_source_color(fill) + @cr.set_line_width(@lw) + first = true + path.each do |p| + if p == '!' + @cr.close_path + first = true + elsif first + @cr.move_to(*p) + first = false + else + @cr.line_to(*p) + end + end + @cr.fill + @cr.stroke + end + + # Draw body parts, really just a rounded line + def draw_body x1,y1,x2,y2 + @cr.set_source_color(:silver) + @cr.set_line_width(@lw) + @cr.set_line_join(Cairo::LINE_JOIN_ROUND) + @cr.move_to(x1,y1) + @cr.line_to(x2,y2) + end + + # Draw a servo box (in rcolor), and black text + def draw_servo x,y,label,rcolor=:black + @cr.set_source_color(rcolor) + @cr.rectangle(x, y, 28, 28); + @cr.set_font_size(16) + @cr.stroke + @cr.move_to(x+4,y+20) + @cr.set_source_color(:black) + @cr.show_text(label); + @cr.stroke + end + + # Draw direction arrow at Y offset + def draw_dirn y=80 + @cr.set_line_join(Cairo::LINE_JOIN_BEVEL) + @cr.set_source_color(:red) + @cr.move_to(100,y) + @cr.set_line_width(12) + @cr.rel_line_to(0, 40) + @cr.stroke + @cr.set_line_width(1) + @cr.move_to(100,y-5) + @cr.rel_line_to(-15, 15) + @cr.rel_line_to(30, 0) + @cr.rel_line_to(-15, -15) + @cr.fill + @cr.stroke + end + + # Draw a circle, perhaps with directional arrows + # lyoffset, lxoffset change label position + def draw_circle x,y,label,dirn=CCW,loc=NE,fill=nil,colour=nil,lyoffset=0,lxoffset=0 + col = (colour||Cairo::Color::ROYAL_BLUE) + @cr.set_font_size(@radius) + @cr.set_line_join(Cairo::LINE_JOIN_MITER) + @cr.set_line_width(3) + + if fill + @cr.set_source_color(fill) + @cr.circle(x,y, @radius) + @cr.fill + @cr.stroke + end + + @cr.set_source_color(col) + @cr.circle(x,y, @radius) + + if dirn != NOARROW + arrow = @radius*0.6 + adelta = arrow*0.12 + x0 = x + y0 = y + dx = 0 + dy = 0 + radj = @radius / Math.sqrt(2) + xadj = yadj = 0 + + case loc + when NE + x0 += radj + y0 -= radj + case dirn + when CW + xadj = yadj = -arrow + dy = adelta + when CCW + xadj = yadj = arrow + dx = -adelta + end + when SE + x0 += radj + y0 += radj + case dirn + when CW + xadj = arrow + yadj = -arrow + dx = -adelta + when CCW + xadj = -arrow + yadj = arrow + dy = -adelta + end + when SW + x0 -= radj + y0 += radj + case dirn + when CW + xadj = yadj = arrow + dy = -adelta + when CCW + xadj = yadj = -arrow + dx = adelta + end + when NW + x0 -= radj + y0 -= radj + case dirn + when CW + xadj = -arrow + yadj = arrow + dx = adelta + when CCW + xadj = arrow + yadj = -arrow + dy = adelta + end + end + @cr.move_to(x0,y0) + @cr.rel_line_to(dx, yadj) + @cr.move_to(x0,y0) + @cr.rel_line_to(xadj, dy) + end + @cr.stroke + @cr.move_to(x-@radius/4+lxoffset,y+@radius/4+lyoffset) + @cr.set_source_color(:black) + @cr.show_text(label); + @cr.stroke + end + + # Reset line styles + def end_body + @cr.set_line_cap Cairo::LINE_CAP_ROUND + @cr.stroke + @cr.set_line_cap Cairo::LINE_CAP_BUTT + end + + # close a model, write out with attribution + def close + @cr.show_page + @surface.finish + @output.rewind + cc=false + File.open(@name, "w") do |f| + @output.each do |l|# + f.puts(l) + unless cc + f.puts("<!-- Public domain (CC-BY-SA if you or your laws insist), generated by Jonathan Hudson's svg_model_motors.rb -->") + cc = true + end + end + end + end +end + + +def render_bi + m = Model.new "bicopter.svg" + m.draw_body 40,100,160,100 + m.end_body + m.draw_circle 40,100,"1",Model::CW,Model::NW + m.draw_circle 160,100,"2",Model::CCW,Model::NE + m.draw_servo 64, 120, "S1" + m.draw_servo 108, 120, "S2" + m.draw_dirn 70 + m.close +end + +def render_tri + m = Model.new "tri.svg" + if TFLAT + m.draw_body 40,40,160,40 + m.draw_body 100,40,100,160 + else + m.draw_body 100,50,40,40 + m.draw_body 100,50,160,40 + m.draw_body 100,50,100,160 + end + m.end_body + m.draw_circle 100,160,"1",Model::CCW,Model::NW + m.draw_circle 160,40,"2",Model::CCW,Model::NW + m.draw_circle 40,40,"3", Model::CCW, Model::NE + m.draw_servo 140, 140, "S1" + m.draw_dirn 70 + m.close +end + +def render_y4 + m = Model.new "y4.svg" + m.draw_circle 100,170,"3",Model::CCW,Model::SE,false,:dark_green,14 + if YFLAT == true + m.draw_body 40,40,160,40 + m.draw_body 100,40,100,140 + else + m.draw_body 100,50,40,40 + m.draw_body 100,50,160,40 + m.draw_body 100,50,100,140 + end + m.end_body + m.draw_circle 160,40,"2",Model::CCW,Model::NE + m.draw_circle 40,40,"4", Model::CW, Model::NW + m.draw_circle 100,140,"1",Model::CW,Model::NE,"#fff8",nil,-10 + + m.draw_dirn 60 + m.close +end + +def render_y6 + m = Model.new "y6.svg" + m.draw_circle 100,170,"4",Model::CW,Model::SW,false,:dark_green,14 + m.draw_circle 30,30,"6",Model::CCW,Model::NE,false,:dark_green,-10 + m.draw_circle 170,30,"5",Model::CCW,Model::NW,false,:dark_green,-10 + if YFLAT == true + m.draw_body 40,50,160,50 + m.draw_body 100,50,100,140 + else + m.draw_body 100,60,40,50 + m.draw_body 100,60,160,50 + m.draw_body 100,60,100,140 + end + m.end_body + m.draw_circle 145,55,"2",Model::CW,Model::NW,"#fff8",nil,12 + m.draw_circle 55,55,"3", Model::CW, Model::NE,"#fff8",nil,12 + m.draw_circle 100,140,"1",Model::CCW,Model::NW,"#fff8",nil,-10 + m.draw_dirn 60 + m.close +end + +def render_vtail + m = Model.new "vtail_quad.svg" + if TFLAT == true + m.draw_body 40,40,160,40 + m.draw_body 100,40,100,180 + else + m.draw_body 100,50,40,40 + m.draw_body 100,50,160,40 + m.draw_body 100,50,100,180 + end + m.draw_body 100,180,140,160 + m.draw_body 100,180,60,160 + m.end_body + m.draw_circle 140,160,"1",Model::CW,Model::SE + m.draw_circle 160,40,"2",Model::CCW,Model::NE + m.draw_circle 60,160,"3",Model::CCW,Model::SW + m.draw_circle 40,40,"4",Model::CW,Model::NW + m.draw_dirn + m.close +end + +def render_atail + m = Model.new "atail_quad.svg" + if TFLAT == true + m.draw_body 40,40,160,40 + m.draw_body 100,40,100,140 + else + m.draw_body 100,50,40,40 + m.draw_body 100,50,160,40 + m.draw_body 100,50,100,140 + end + m.draw_body 100,140,140,160 + m.draw_body 100,140,60,160 + m.end_body + m.draw_circle 60,160,"1",Model::CCW,Model::SW + m.draw_circle 160,40,"2",Model::CCW,Model::NE + m.draw_circle 140,160,"3",Model::CW,Model::SE + m.draw_circle 40,40,"4",Model::CW,Model::NW + m.draw_dirn + m.close +end + +def render_octox8 # just x8 surely? + m = Model.new "octo_x8.svg" + + m.draw_circle 170,170,"5",Model::CCW,Model::NE,false,:dark_green,14,8 + m.draw_circle 170,30,"6",Model::CW,Model::SE,false,:dark_green,-10,8 + m.draw_circle 30,170,"7",Model::CW,Model::NW,false,:dark_green,14,-10 + m.draw_circle 30,30,"8",Model::CCW,Model::SW,false,:dark_green,-10,-10 + + m.draw_body 50,50,150,150 + m.draw_body 50,150,150,50 + m.end_body + m.draw_circle 150,150,"1",Model::CW,Model::SW,"#fff8",nil,-10 + m.draw_circle 150,50,"2",Model::CCW,Model::NW,"#fff8",nil,12 + m.draw_circle 50,150,"3",Model::CCW,Model::SE,"#fff8",nil,-10 + m.draw_circle 50,50,"4",Model::CW,Model::NE,"#fff8",nil,12 + m.draw_dirn + m.close +end + +def render_quadx + m = Model.new "quad_x.svg" + m.draw_body 40,40,160,160 + m.draw_body 40,160,160,40 + m.end_body + m.draw_circle 160,160,"1",Model::CW,Model::SE + m.draw_circle 160,40,"2",Model::CCW,Model::NE + m.draw_circle 40,160,"3",Model::CCW,Model::SW + m.draw_circle 40,40,"4",Model::CW,Model::NW + m.draw_dirn + m.close +end + +def render_quadp + m = Model.new "quad_p.svg" + m.draw_body 40,100,160,100 + m.draw_body 100,40,100,160 + m.end_body + m.draw_circle 100,160,"1",Model::CW,Model::SW + m.draw_circle 160,100,"2",Model::CCW,Model::NE + m.draw_circle 100,40,"4",Model::CW,Model::NE + m.draw_circle 40,100,"3",Model::CCW,Model::SW + m.draw_dirn + m.close +end + +def render_hexp + s = ShapeFarm.new + p = s.generate 100, 100, 60, 6 + + m = Model.new "hex_p.svg" + m.draw_body *p[0],*p[3] + m.draw_body *p[1],*p[4] + m.draw_body *p[2],*p[5] + m.end_body + m.radius = 24 + m.draw_circle *p[0],"5",Model::CCW,Model::NW + m.draw_circle *p[1],"2",Model::CW,Model::NE + m.draw_circle *p[2],"1",Model::CCW,Model::SE + m.draw_circle *p[3],"6",Model::CW,Model::SW + m.draw_circle *p[4],"3",Model::CCW,Model::SW + m.draw_circle *p[5],"4",Model::CW,Model::NW + m.draw_dirn + m.close +end + +def render_hexx + s = ShapeFarm.new + p = s.generate 100, 100, 60, 6, 30 + + m = Model.new "hex_x.svg" + m.draw_body *p[0],*p[3] + m.draw_body *p[1],*p[4] + m.draw_body *p[2],*p[5] + m.end_body + m.radius = 24 + m.draw_circle *p[0],"2",Model::CCW,Model::NE + m.draw_circle *p[1],"5",Model::CW,Model::SE + m.draw_circle *p[2],"1",Model::CCW,Model::SE + m.draw_circle *p[3],"3",Model::CW,Model::SW + m.draw_circle *p[4],"6",Model::CCW,Model::SW + m.draw_circle *p[5],"4",Model::CW,Model::NW + m.draw_dirn + m.close +end + +def render_octx + s = ShapeFarm.new + p = s.generate 100, 100, 70, 8, 22.5 + + m = Model.new "octo_flat_x.svg" + m.lw = 20 + m.radius = 20 + m.draw_body *p[0],*p[4] + m.draw_body *p[1],*p[5] + m.draw_body *p[2],*p[6] + m.draw_body *p[3],*p[7] + m.end_body + m.draw_circle *p[0],"2",Model::CCW,Model::NE + m.draw_circle *p[1],"6",Model::CW,Model::NE + m.draw_circle *p[2],"3",Model::CCW,Model::SE + m.draw_circle *p[3],"7",Model::CW,Model::SE + m.draw_circle *p[4],"4",Model::CCW,Model::SW + m.draw_circle *p[5],"8",Model::CW,Model::SW + m.draw_circle *p[6],"1",Model::CCW,Model::NW + m.draw_circle *p[7],"5",Model::CW,Model::NW + m.draw_dirn + m.close +end + +def render_octp + s = ShapeFarm.new + p = s.generate 100, 100, 70, 8 + + m = Model.new "octo_flat_p.svg" + m.lw = 20 + m.radius = 20 + + m.draw_body *p[0],*p[4] + m.draw_body *p[1],*p[5] + m.draw_body *p[2],*p[6] + m.draw_body *p[3],*p[7] + m.end_body + + m.draw_circle *p[0],"2",Model::CW,Model::NE + m.draw_circle *p[1],"6",Model::CCW,Model::NE + m.draw_circle *p[2],"3",Model::CW,Model::SE + m.draw_circle *p[3],"7",Model::CCW,Model::SE + m.draw_circle *p[4],"4",Model::CW,Model::SW + m.draw_circle *p[5],"8",Model::CCW,Model::SW + m.draw_circle *p[6],"1",Model::CW,Model::NW + m.draw_circle *p[7],"5",Model::CCW,Model::NW + m.draw_dirn + m.close +end + +def render_aero + m = Model.new "airplane.svg" + m.lw = 1 + +# For easy? of understanding, split into parts +# Nose +# m.draw_path([[85,20], [80,40], [120,40], [115,20],'!'],:silver, true) +# Wing +# m.draw_path([[80,40], [20,60], [20,100], [70,80], [130,80], [180,100], +# [180,60], [120,40],'!'], :silver, true) +# Aft +# m.draw_path([[80,80],[90,150],[110,150],[120,80],'!'], :silver, true) +# Tail +# m.draw_path([[90,150], [50,155], [50,175], [150,175], [150,155], [110,150],'!'], :silver, true) + + m.draw_path([[85,20], [80,40], [20,60], [20,100], [70,80], + [80,80], [90,150], [50,155], [50,175], + [150,175], [150,155], [110,150],[120,80],[130,80], + [180,100], [180,60], [120,40], [115,20],'!'],:silver, true) + m.draw_path([[20,80],[20,100],[70,80],[70,60], '!'], :red) + m.draw_path([[180,80],[180,100],[130,80],[130,60], '!'], :green) + m.draw_path([[50,165], [50,175], [150,175], [150,165],'!'],:orange) + m.draw_path([[100,140], [95,150], [100,175], [105,150],'!'],:black) + m.end_body + m.radius = 14 + m.draw_circle 100,15,"1/2",Model::NOARROW,Model::SE, false, nil, 0, -9 + m.draw_servo 30, 100, " 3", :red + m.draw_servo 142, 100, " 4", :green + + m.draw_servo 64, 134, " 5", :black + m.draw_servo 154, 168, " 6", :orange + m.draw_dirn 50 + m.close +end + +def render_wing + m = Model.new "flying_wing.svg" + m.lw = 1 + m.draw_path([[80,20],[20,80],[20,120],[70,80],[130,80], [180,120],[180,80], + [120,20],'!'], :silver) + m.draw_path([[20,100],[20,120],[70,80],[70,60], '!'], :red) + m.draw_path([[180,100],[180,120],[130,80],[130,60], '!'], :green) + + m.draw_servo 30, 120, " 3", :red + m.draw_servo 142, 120, " 4", :green + m.draw_circle 100,110,"1/2",Model::NOARROW,Model::SE, false, nil, 0, -16 + m.draw_dirn 30 + m.close +end + +render_bi +render_tri +render_quadx +render_quadp +render_hexp +render_hexx +render_octx +render_octp +render_vtail +render_atail +render_y4 +render_y6 +render_octox8 +render_aero +render_wing |