aurora-sketch 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a30dbae9c584de863771b8fc55d168ce68587f64
4
+ data.tar.gz: 08d3ee908cac65462bd649998e10abee1c99f971
5
+ SHA512:
6
+ metadata.gz: 7cbe472f677cbab278b3c2988e4521ef056e17f0a857b4fd8a64fac247d5b8211f9829a911eacecbc67e24d56e478aab02ca349ea80c9e065ae4d62d6e7cd923
7
+ data.tar.gz: 66f03e9b9d37720623e616825e9731baf350bb19d523dc6d14915cbc26afead7e623c3a39d20bc668739c3dbc0a305a7969784d0ef59ef9dbdcb51d38d17353b
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ .yardoc
4
+ Gemfile.lock
5
+ pkg/*
6
+ doc
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ group :test do
6
+ gem 'rake'
7
+ end
data/README.markdown ADDED
@@ -0,0 +1,86 @@
1
+ Sketching with Ruby
2
+ ===================
3
+
4
+ [![Build Status](https://travis-ci.org/bfoz/sketch.png)](https://travis-ci.org/bfoz/sketch)
5
+
6
+ Classes and methods for programmatically creating, manipulating, and exporting
7
+ simple geometric drawings. This gem is primarily intended to support mechanical
8
+ design generation, but it can also handle the doodling that you used to do in
9
+ your notebook while stuck in that really boring class (you know the one).
10
+
11
+ At its most basic, Sketch is a container for Geometry objects. The classes in
12
+ this gem are based on the classes provided by the Geometry gem, but have some
13
+ extra magic applied to support transformations, constraints, etc. Like the
14
+ Geometry module, Sketch assumes that primitives lie in 2D space, but doesn't
15
+ enforce that constraint. Please let me know if you find cases that don't work in
16
+ higher dimensions and I'll do my best to fix them.
17
+
18
+ License
19
+ -------
20
+
21
+ Copyright 2012-2014 Brandon Fosdick <bfoz@bfoz.net> and released under the BSD license.
22
+
23
+ Examples
24
+ --------
25
+
26
+ A basic sketch with a single circle
27
+
28
+ ```ruby
29
+ require 'sketch'
30
+
31
+ sketch = Sketch.new do
32
+ circle center:[0,0], diameter:5 # Center = [0,0], Radius = 5
33
+ end
34
+ ```
35
+
36
+ The same sketch again, but a little more square
37
+
38
+ ```ruby
39
+ Sketch.new { rectangle origin:[0,0], size:[1,1] }
40
+ ```
41
+
42
+ You can also group elements for convenience
43
+
44
+ ```ruby
45
+ Sketch.new do
46
+ group origin:[0,2] do
47
+ circle center:[-2, 0], radius:1
48
+ circle center:[2, 0], radius:1
49
+ end
50
+ circle center:[0, -1], radius:1
51
+ end
52
+ ```
53
+
54
+ There's a shortcut for when you're only creating a group to translate some elements
55
+
56
+ ```ruby
57
+ Sketch.new do
58
+ translate [0,2] do
59
+ circle center:[-2, 0], radius:1
60
+ circle center:[2, 0], radius:1
61
+ end
62
+ circle center:[0, -1], radius:1
63
+ end
64
+ ```
65
+
66
+ Sometimes you feel like a group, sometimes you feel like a layout.
67
+
68
+ ```ruby
69
+ Sketch.new do
70
+ layout :horizontal do
71
+ circle center:[-2, 0], radius:1
72
+ circle center:[2, 0], radius:1
73
+ end
74
+ end
75
+ ```
76
+
77
+ The layout command also takes options for spacing and alignment. For example, to add one unit of extra space between each element, and align them with the X-axis:
78
+
79
+ ```ruby
80
+ Sketch.new do
81
+ layout :horizontal, spacing:1, align: :bottom do
82
+ circle center:[-2, 0], radius:1
83
+ circle center:[2, 0], radius:1
84
+ end
85
+ end
86
+ ```
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ task :default => :test
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.libs.push "lib"
8
+ t.test_files = FileList['test/**/*.rb']
9
+ t.verbose = true
10
+ end
11
+
12
+ task :fixdates do
13
+ branch = `git branch --no-color -r --merged`.strip
14
+ `git fix-dates #{branch}..HEAD`
15
+ end
16
+
17
+ task :fixdates_f do
18
+ branch = `git branch --no-color -r --merged`.strip
19
+ `git fix-dates -f #{branch}..HEAD`
20
+ end
21
+
22
+ task :trim_whitespace do
23
+ system(%Q[git status --short | awk '{if ($1 != "D" && $1 != "R") print $2}' | grep -e '.*\.rb$' | xargs sed -i '' -e 's/[ \t]*$//g;'])
24
+ end
25
+
26
+ task :docs do
27
+ `yard`
28
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "aurora-sketch"
6
+ s.version = '0.0.1'
7
+ s.authors = ["Brandon Fosdick", "Meseker Yohannes"]
8
+ s.email = ["bfoz@bfoz.net"]
9
+ s.homepage = "http://github.com/bfoz/sketch"
10
+ s.summary = %q{2D mechanical sketches}
11
+ s.description = %q{Sketches used in the creation of mechanical designs}
12
+
13
+ s.rubyforge_project = "sketch"
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ s.add_dependency 'aurora-geometry', '0.0.1'
21
+ end
@@ -0,0 +1,107 @@
1
+ module Geometry
2
+ module DSL
3
+ =begin rdoc
4
+ When you want to draw things that are made of lots and lots of lines, this is how you do it.
5
+
6
+ == Requirements
7
+ This module is intended to be included into a Class, and that Class must provide
8
+ some infrastructure. It must provide a push method for pushing new elements, a
9
+ first method that returns the first vertex in the {Polyline}, and a last method
10
+ that returns the last vertex.
11
+
12
+ == Usage
13
+ begin
14
+ start_at [0,0]
15
+ move_y 1 # Go up 1 unit
16
+ right 1
17
+ down 1
18
+ left 1 # Close the box
19
+ close # Unnecessary in this case
20
+ end
21
+ =end
22
+ module Polyline
23
+ BuildError = Class.new(StandardError)
24
+
25
+ # Close the {Polyline} with a {Line}, if it isn't already closed
26
+ def close
27
+ move_to(first) unless closed?
28
+ end
29
+
30
+ # @return [Bool] True if the {Polyline} is closed, otherwise false
31
+ def closed?
32
+ first == last
33
+ end
34
+
35
+ # Draw a line to the given {Point}
36
+ # @param [Point] The {Point} to draw a line to
37
+ def move_to(point)
38
+ push Point[point]
39
+ end
40
+
41
+ # Move the specified distance along the X axis
42
+ # @param [Number] distance The distance to move
43
+ def move_x(distance)
44
+ push (last || PointZero) + Point[distance, 0]
45
+ end
46
+
47
+ # Move the specified distance along the Y axis
48
+ # @param [Number] distance The distance to move
49
+ def move_y(distance)
50
+ push (last || PointZero) + Point[0, distance]
51
+ end
52
+
53
+ # Specify a starting point. Can't be specified twice, and only required if no other entities have been added.
54
+ # #param [Point] point The starting point
55
+ def start_at(point)
56
+ raise BuildError, "Can't specify a start point more than once" if first
57
+ push Point[point]
58
+ end
59
+
60
+ # @group Relative Movement
61
+
62
+ # Move the specified distance along the +Y axis
63
+ # @param [Number] distance The distance to move in the +Y direction
64
+ def up(distance)
65
+ move_y distance
66
+ end
67
+
68
+ # Move the specified distance along the -Y axis
69
+ # @param [Number] distance The distance to move in the -Y direction
70
+ def down(distance)
71
+ move_y -distance
72
+ end
73
+
74
+ # Move the specified distance along the -X axis
75
+ # @param [Number] distance The distance to move in the -X direction
76
+ def left(distance)
77
+ move_x -distance
78
+ end
79
+
80
+ # Move the specified distance along the +X axis
81
+ # @param [Number] distance The distance to move in the +X direction
82
+ def right(distance)
83
+ move_x distance
84
+ end
85
+
86
+ # Draw a vertical line to the given y-coordinate while preserving the
87
+ # x-coordinate of the previous {Point}
88
+ # @param y [Number] the y-coordinate to move to
89
+ def vertical_to(y)
90
+ push Point[last.x, y]
91
+ end
92
+ alias :down_to :vertical_to
93
+ alias :up_to :vertical_to
94
+
95
+ # Draw a horizontal line to the given x-coordinate while preserving the
96
+ # y-coordinate of the previous {Point}
97
+ # @param x [Number] the x-coordinate to move to
98
+ def horizontal_to(x)
99
+ push [x, last.y]
100
+ end
101
+ alias :left_to :horizontal_to
102
+ alias :right_to :horizontal_to
103
+
104
+ # @endgroup
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,64 @@
1
+ module Geometry
2
+ module DSL
3
+ =begin
4
+ An implementation of {http://en.wikipedia.org/wiki/Turtle_graphics Logo Turtle-style} commands
5
+
6
+ == Examples
7
+ Draw a square...
8
+
9
+ PolygonBuilder.new.evaluate do
10
+ start_at [0,0] # Every journey begins with a single point...
11
+ move_to [1,0] # Draw a line to a new point
12
+ turn_left 90
13
+ move 1 # Same as forward 1
14
+ turn_left 90
15
+ forward 1
16
+ end
17
+
18
+ The same thing, but more succint:
19
+
20
+ PolygonBuilder.new.evaluate do
21
+ start_at [0,0]
22
+ move [1,0] # Move and draw using a vector distance
23
+ move [0,1]
24
+ move [-1,0]
25
+ end
26
+ =end
27
+ module Turtle
28
+ # Turn left by the given number of degrees
29
+ def turn_left(angle)
30
+ @direction += angle if @direction
31
+ @direction ||= angle
32
+ end
33
+
34
+ # Turn right by the given number of degrees
35
+ def turn_right(angle)
36
+ turn_left -angle
37
+ end
38
+
39
+ # Draw a line by moving a given distance
40
+ # @overload move(Numeric)
41
+ # Same as forward(Numeric)
42
+ # @overload move(Array)
43
+ # @overload move(x,y)
44
+ def move(*distance)
45
+ return forward(*distance) if (1 == distance.size) && distance[0].is_a?(Numeric)
46
+
47
+ if distance[0].is_a?(Vector)
48
+ distance = distance[0]
49
+ elsif distance[0].is_a?(Array)
50
+ distance = Vector[*(distance[0])]
51
+ end
52
+
53
+ push(last + distance)
54
+ end
55
+
56
+ # Move the specified distance in the current direction
57
+ def forward(distance)
58
+ @direction ||= 0 # direction defaults to 0
59
+ radians = @direction * Math::PI / 180
60
+ push(last + Vector[distance*Math.cos(radians),distance*Math.sin(radians)])
61
+ end
62
+ end
63
+ end
64
+ end
data/lib/sketch.rb ADDED
@@ -0,0 +1,180 @@
1
+ require 'geometry'
2
+ require_relative 'sketch/builder'
3
+ require_relative 'sketch/point.rb'
4
+
5
+ =begin
6
+ A Sketch is a container for Geometry objects.
7
+ =end
8
+
9
+ class Sketch
10
+ attr_reader :elements
11
+ attr_accessor :transformation
12
+
13
+ Arc = Geometry::Arc
14
+ Circle = Geometry::Circle
15
+ Line = Geometry::Line
16
+ Rectangle = Geometry::Rectangle
17
+ Square = Geometry::Square
18
+
19
+ def initialize(&block)
20
+ @elements = []
21
+ instance_eval(&block) if block_given?
22
+ end
23
+
24
+ # Define a class parameter
25
+ # @param [Symbol] name The name of the parameter
26
+ # @param [Proc] block A block that evaluates to the desired value of the parameter
27
+ def self.define_parameter name, &block
28
+ define_method name do
29
+ @parameters ||= {}
30
+ @parameters.fetch(name) { |k| @parameters[k] = instance_eval(&block) }
31
+ end
32
+ end
33
+
34
+ # Define an instance parameter
35
+ # @param [Symbol] name The name of the parameter
36
+ # @param [Proc] block A block that evaluates to the desired value of the parameter
37
+ def define_parameter name, &block
38
+ singleton_class.send :define_method, name do
39
+ @parameters ||= {}
40
+ @parameters.fetch(name) { |k| @parameters[k] = instance_eval(&block) }
41
+ end
42
+ end
43
+
44
+ # @group Accessors
45
+ # @attribute [r] bounds
46
+ # @return [Rectangle] The smallest axis-aligned {Rectangle} that encloses all of the elements
47
+ def bounds
48
+ Rectangle.new(*minmax)
49
+ end
50
+
51
+ # @attribute [r] first
52
+ # @return [Geometry] first the first Geometry element of the {Sketch}
53
+ def first
54
+ elements.first
55
+ end
56
+
57
+ # @attribute [r] geometry
58
+ # @return [Array] All elements rendered into Geometry objects
59
+ def geometry
60
+ @elements
61
+ end
62
+
63
+ # @attribute [r] last
64
+ # @return [Geometry] the last Geometry element of the {Sketch}
65
+ def last
66
+ elements.last
67
+ end
68
+
69
+ # @attribute [r] max
70
+ # @return [Point]
71
+ def max
72
+ minmax.last
73
+ end
74
+
75
+ # @attribute [r] min
76
+ # @return [Point]
77
+ def min
78
+ minmax.first
79
+ end
80
+
81
+ # @attribute [r] minmax
82
+ # @return [Array<Point>]
83
+ def minmax
84
+ return [nil, nil] unless @elements.size != 0
85
+
86
+ memo = @elements.map {|e| e.minmax }.reduce {|memo, e| [Point[[memo.first.x, e.first.x].min, [memo.first.y, e.first.y].min], Point[[memo.last.x, e.last.x].max, [memo.last.y, e.last.y].max]] }
87
+ if self.transformation
88
+ if self.transformation.has_rotation?
89
+ # If the transformation has a rotation, convert the minmax into a bounding rectangle, rotate it, then find the new minmax
90
+ point1, point3 = Point[memo.last.x, memo.first.y], Point[memo.first.x, memo.last.y]
91
+ points = [memo.first, point1, memo.last, point3].map {|point| self.transformation.transform(point) }
92
+ points.reduce([points[0], points[2]]) {|memo, e| [Point[[memo.first.x, e.x].min, [memo.first.y, e.y].min], Point[[memo.last.x, e.x].max, [memo.last.y, e.y].max]] }
93
+ else
94
+ memo.map {|point| self.transformation.transform(point) }
95
+ end
96
+ else
97
+ memo
98
+ end
99
+ end
100
+
101
+ # @attribute [r] size
102
+ # @return [Size] The size of the {Rectangle} that bounds all of the {Sketch}'s elements
103
+ def size
104
+ Size[self.minmax.reverse.reduce(:-).to_a]
105
+ end
106
+
107
+ # @endgroup
108
+
109
+ # Append the given {Geometry} element and return the {Sketch}
110
+ # @param element [Geometry] the {Geometry} element to append
111
+ # @param args [Array] optional transformation parameters
112
+ # @return [Sketch]
113
+ def push(element, *args)
114
+ options, args = args.partition {|a| a.is_a? Hash}
115
+ options = options.reduce({}, :merge)
116
+
117
+ if options and (options.size != 0) and (element.respond_to? :transformation)
118
+ element.transformation = Geometry::Transformation.new options
119
+ end
120
+
121
+ @elements.push(element)
122
+ self
123
+ end
124
+
125
+ # @group Geometry creation
126
+
127
+ # Create and append a new {Arc} object
128
+ # @param (see Arc#initialize)
129
+ # @return [Arc]
130
+ def add_arc(*args)
131
+ @elements.push(Arc.new(*args)).last
132
+ end
133
+
134
+ # Create and append a new {Circle} object given a center point and radius
135
+ # @param [Point] center The circle's center point
136
+ # @param [Number] radius The circle's radius
137
+ # @return [Circle] A new {Circle}
138
+ def add_circle(*args)
139
+ @elements.push Circle.new(*args)
140
+ @elements.last
141
+ end
142
+
143
+ # Create a Line using any arguments that work for {Geometry::Line}
144
+ def add_line(*args)
145
+ @elements.push Line[*args]
146
+ @elements.last
147
+ end
148
+
149
+ # Create a Point with any arguments that work for {Geometry::Point}
150
+ def add_point(*args)
151
+ @elements.push Point[*args]
152
+ @elements.last
153
+ end
154
+
155
+ # Create a {Rectangle}
156
+ def add_rectangle(*args)
157
+ @elements.push Rectangle.new(*args)
158
+ @elements.last
159
+ end
160
+
161
+ # Create a Square with sides of the given length
162
+ # @param [Numeric] length The length of the sides of the square
163
+ def add_square(length)
164
+ push Geometry::CenteredSquare.new [0,0], length
165
+ @elements.last
166
+ end
167
+
168
+ # Create and add a {Triangle}
169
+ # @param (see Triangle::new)
170
+ def add_triangle(*args)
171
+ push Geometry::Triangle.new *args
172
+ end
173
+
174
+ # @endgroup
175
+
176
+ end
177
+
178
+ def Sketch(&block)
179
+ Sketch::Builder.new &block
180
+ end