wicket 0.0.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +2 -5
- data/Gemfile +2 -0
- data/README.md +132 -12
- data/changelog.md +12 -0
- data/lib/wicket.rb +22 -0
- data/lib/wicket/cartesian.rb +23 -0
- data/lib/wicket/command.rb +47 -14
- data/lib/wicket/commands/bezier_curve.rb +75 -0
- data/lib/wicket/commands/c.rb +24 -0
- data/lib/wicket/commands/cubic_bezier.rb +20 -0
- data/lib/wicket/commands/h.rb +10 -14
- data/lib/wicket/commands/l.rb +6 -18
- data/lib/wicket/commands/m.rb +6 -18
- data/lib/wicket/commands/q.rb +21 -0
- data/lib/wicket/commands/quadratic_bezier.rb +15 -0
- data/lib/wicket/commands/s.rb +37 -0
- data/lib/wicket/commands/t.rb +34 -0
- data/lib/wicket/commands/v.rb +10 -13
- data/lib/wicket/commands/z.rb +9 -9
- data/lib/wicket/configuration.rb +26 -0
- data/lib/wicket/coordinate.rb +21 -0
- data/lib/wicket/subpath.rb +7 -3
- data/lib/wicket/subpoint.rb +39 -0
- data/lib/wicket/svg_path.rb +25 -10
- data/lib/wicket/utilities.rb +60 -0
- data/lib/wicket/version.rb +1 -1
- data/spec/coordinate_spec.rb +28 -0
- data/spec/svg_path_spec.rb +26 -107
- data/spec/test_cases.yml +91 -0
- data/spec/utilities_spec.rb +91 -0
- metadata +21 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bd072dfef6d4eb66f52974a60e5b6a1df82c24a3
|
4
|
+
data.tar.gz: a3e29991b47184b46eff7801a3678a7dc7c4d2fd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d22c55c1e9ade2cc8d48e7d5c2a579e5e88f980b81dc7ae67a3ee89a58260eefa49b5828a163dd09a89906162dfaf33907b5307ffdc0593aadb0c98523a24792
|
7
|
+
data.tar.gz: b38949f08bd4eaaa4966cb54f8dbe36ab48693edef333373d558a36418e77e22bd5b9051edb6778960384782e4b4e183e801f908d655b52205b24e119007e9f0
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -8,17 +8,20 @@ Notable features include:
|
|
8
8
|
- Translating line paths into WKT Polygons or Multipolygons
|
9
9
|
- Accepting absolute or relative path commands
|
10
10
|
- Inversing Y axis measurements (SVG y coordinates decrease as you go up)
|
11
|
+
- Translates quadratic and cubic curve commands into straight line segments
|
12
|
+
- Curve definition translation controls
|
13
|
+
- Numeric precision controls
|
11
14
|
- Decimal math for increased accuracy
|
12
15
|
|
13
16
|
Future possible features could include:
|
14
|
-
-
|
17
|
+
- The arc "A" command
|
15
18
|
- Translating non closed paths into Linestrings and Multilines
|
16
19
|
- Specifying paths for polygons which are exlcusions from the area (donut holes)
|
17
20
|
- Translating WKT to SVG (although PostGIS already does this)
|
18
21
|
|
19
22
|
If you want to see these or any other features, feel free to make an issue or pull request.
|
20
23
|
|
21
|
-
Wicket is similar in concept to [SVG-to-WKT.js](https://github.com/davidmcclure/svg-to-wkt), although that project operates within the browser
|
24
|
+
Wicket is similar in concept to [SVG-to-WKT.js](https://github.com/davidmcclure/svg-to-wkt), although that project operates within the browser. It also owes credit to [Savage](https://github.com/awebneck/savage) as a reference for the SVG parser. If you need to edit SVG paths, I would recommend checking it out.
|
22
25
|
|
23
26
|
## Installation
|
24
27
|
|
@@ -34,35 +37,152 @@ Or install it yourself as:
|
|
34
37
|
|
35
38
|
$ gem install wicket
|
36
39
|
|
40
|
+
## Configuration
|
41
|
+
|
42
|
+
You can pass configuration parameters to Wicket at three times:
|
43
|
+
|
44
|
+
- At load time, using an initializer
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
Wicket.configure do |w|
|
48
|
+
w.min_angle = 165
|
49
|
+
w.min_angle_unit = :degrees
|
50
|
+
end
|
51
|
+
```
|
52
|
+
|
53
|
+
- At instantiation time (overrides the initializer)
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
Wicket::SVGPath.new("M10 10H20l10 10H10z",
|
57
|
+
min_angle: 165,
|
58
|
+
min_angle_unit: :degrees
|
59
|
+
)
|
60
|
+
```
|
61
|
+
|
62
|
+
- At conversion time (overrides the initializer and instantiation)
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
path = Wicket::SVGPath.new("M10 10H20l10 10H10z")
|
66
|
+
path.to_polygon(min_angle: 165, min_angle_unit: :degrees)
|
67
|
+
```
|
68
|
+
|
69
|
+
### Configuration Options
|
70
|
+
|
71
|
+
There are currently two configuration options:
|
72
|
+
|
73
|
+
1. `min_angle`
|
74
|
+
|
75
|
+
This is the minimum angle allowed when translating a curve into a series of straight lines. A higher minimum will require more line segments, more vertices, and more text, but it will get you a more accurate line.
|
76
|
+
|
77
|
+
There are 4 ways to describe an angle to Wicket, and luckily Wicket is pretty smart about figuring out which one you want, although, as noted below, you can explicitly state which one you want.
|
78
|
+
|
79
|
+
1. `decimal_percentage`
|
80
|
+
|
81
|
+
If the `min_angle` is greater than 0 and less than 1, Wicket assumes you are expressing a decimal percentage of the maximum total angle (180 degrees, or Pi radians). For example, 0.9 would indicate that you only want to allow angles of 0.9 * Pi radians or 0.9 * 180 degrees.
|
82
|
+
|
83
|
+
2. `radians`
|
84
|
+
|
85
|
+
If the `min_angle` is greater than or equal to 1 and less than Pi, it will assume you are talking in radians.
|
86
|
+
|
87
|
+
3. `percentage`
|
88
|
+
|
89
|
+
If the `min_angle` is greater than or equal to Pi, and less than 100 it will assume you are talking in percentage terms. This is the same as #1 except multiplied by 100.
|
90
|
+
|
91
|
+
4. `degrees`
|
92
|
+
|
93
|
+
If the `min_angle` is greater than or equal to 100, but less than 180, it will assume you are talking in degrees.
|
94
|
+
|
95
|
+
Obviously, this must be a numeric value greater than 0 and less than 180.
|
96
|
+
|
97
|
+
2. `min_angle_unit`
|
98
|
+
|
99
|
+
As noted above, this should rarely be necessary, but sometimes it can be included for explcitness in the code, or in rare cases, used to override Wicket's guess as to what unit you are using. For example, if you for some reason wanted the `min_angle` to be 0.5 radians, you could explicitly state so and Wicket would comply. This would, however, result in a pretty poorly fit curve, since the above guesses cover all obtuse angles in all four formats.
|
100
|
+
|
101
|
+
This option accepts the same four arguments as above: `decimal_percentage`,`radians`, `percentage`, and `degrees`
|
102
|
+
|
103
|
+
3. `scale`
|
104
|
+
|
105
|
+
This is the number of digits allowed after the decimal point, exactly as you would in a Rails migration for a decimal column. Note that this is only used for formatting, all calculations are done to the maximum possible precision.
|
106
|
+
|
107
|
+
If scale is omitted, Wicket will use the greatest scale of all the input points provided. Thus:
|
108
|
+
|
109
|
+
```
|
110
|
+
"M0,0H100V100H0z" # => 0 scale
|
111
|
+
"M0,0H100V100H0.0z" # => 1 scale
|
112
|
+
"M0,0H100V100H0.00z" # => 2 scale
|
113
|
+
"M0.000,0H100V100H0.00z" # => 3 scale
|
114
|
+
```
|
115
|
+
|
37
116
|
## Usage
|
38
117
|
|
118
|
+
### Linear paths
|
39
119
|
```ruby
|
40
120
|
# one subpath
|
41
121
|
path = Wicket::SVGPath.new("M10 10H20l10 10H10z")
|
42
122
|
path.to_polygon
|
43
|
-
# => "POLYGON((10
|
123
|
+
# => "POLYGON((10 -10,20 -10,30 -20,10 -20,10 -10))"
|
44
124
|
path.to_multipolygon
|
45
|
-
# => "MULTIPOLYGON(((10
|
125
|
+
# => "MULTIPOLYGON(((10 -10,20 -10,30 -20,10 -20,10 -10)))"
|
46
126
|
|
47
127
|
# two subpaths
|
48
128
|
path = Wicket::SVGPath.new("M10 10H20l10 10H10z M100 100h10v10h-10z")
|
49
129
|
path.to_polygon # ONLY THE FIRST SUBPATH!
|
50
|
-
# => "POLYGON((10
|
130
|
+
# => "POLYGON((10 -10,20 -10,30 -20,10 -20,10 -10))"
|
131
|
+
path.to_multipolygon # both subpaths
|
132
|
+
# => "MULTIPOLYGON(((10 -10,30 -10,40 -10,50 -20,40 -20,10 -10)),((100 -100,110 -100,110 -110,100 -110,100 -100)))
|
133
|
+
|
134
|
+
```
|
135
|
+
|
136
|
+
### Curved paths
|
137
|
+
```ruby
|
138
|
+
# cubic
|
139
|
+
path = Wicket::SVGPath.new("M100 100,c0 200,200 200,200 0z")
|
140
|
+
path.to_polygon
|
141
|
+
# => "POLYGON((100 -100,102 -135,108 -165,131 -212,146 -228,163 -240,181 -247,200 -250,218 -247,236 -240,253 -228,268 -212,281 -191,291 -165,300 -100,100 -100))"
|
142
|
+
path.to_multipolygon
|
143
|
+
# => "MULTIPOLYGON(((100 -100,102 -135,108 -165,131 -212,146 -228,163 -240,181 -247,200 -250,218 -247,236 -240,253 -228,268 -212,281 -191,291 -165,300 -100,100 -100)))"
|
144
|
+
|
145
|
+
# quadratic
|
146
|
+
path = Wicket::SVGPath.new("M10 10,Q110 210 210 10z")
|
147
|
+
path.to_polygon
|
148
|
+
# => "POLYGON((10 -10,35 -53,60 -85,72 -95,85 -103,97 -108,110 -110,122 -108,135 -103,147 -95,160 -85,185 -53,210 -10,10 -10))"
|
51
149
|
path.to_multipolygon # both subpaths
|
52
|
-
# => "MULTIPOLYGON(((10
|
150
|
+
# => "MULTIPOLYGON(((10 -10,35 -53,60 -85,72 -95,85 -103,97 -108,110 -110,122 -108,135 -103,147 -95,160 -85,185 -53,210 -10,10 -10)))
|
151
|
+
|
152
|
+
```
|
153
|
+
|
154
|
+
## Debugging/Proof
|
155
|
+
|
156
|
+
Because this is all pretty hard to keep track of in your head, you can call `#to_svg` on any path, and it will output a SVG of your original path in blue and a representation of the WKT in green for visual comparison, ready to paste into a page or JSFiddle or something. You may have to adjust the viewbox to make sure the path is visible. `#to_svg` takes all the same parameters as `#to_polygon`.
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
path = Wicket::SVGPath.new("M0 0,C0 200,200 200,200 0z")
|
160
|
+
puts path.to_svg
|
161
|
+
# <svg>
|
162
|
+
# <path d="M0 0,C0 200,200 200,200 0z" style="fill: slateblue; opacity:0.2"/>
|
163
|
+
# <path d="M0,0 L 2,35 8,65 31,112 46,128 63,140 81,147 100,150 118,147 136,140 153,128 168,112 181,91 191,65 200,0 Z" style="fill:none;stroke:lawngreen" stroke-weight="2"/>
|
164
|
+
# </svg>
|
53
165
|
|
54
166
|
```
|
55
167
|
|
56
168
|
## Gotchas
|
57
169
|
|
58
|
-
-
|
170
|
+
- Be careful when using curves that you have an appropriate scale set for the magnitude of the numbers you are using. If you are dealing with small numbers, curves, and your inputs are integers, all the points will be rounded to integers. You probably don't want that.
|
171
|
+
|
172
|
+
```ruby
|
173
|
+
path = Wicket::SVGPath.new("M0,0Q1,1,2,0z")
|
174
|
+
path.to_polygon # probably not what you want
|
175
|
+
# => "POLYGON((0 0,0 0,0 0,0 0,1 0,1 0,1 0,1 0,2 0,0 0))"
|
176
|
+
path.to_polygon(scale: 4) # much better
|
177
|
+
# => "POLYGON((0.0 -0.0,0.25 -0.2188,0.5 -0.375,0.75 -0.4688,1.0 -0.5,1.25 -0.4688,1.5 -0.375,1.75 -0.2188,2.0 -0.0,0.0 -0.0))"
|
178
|
+
```
|
179
|
+
- Wicket assumes that a move command (M or m) is part of the polygon edge. It just so happened that a lot of the data I was working with when creating this project was formatted that way. If your cursor moves around a lot and does not intend to define these as edges, be aware.
|
59
180
|
- Polygons are assumed to have no holes, thus everything inside the path is part of the polygon.
|
60
181
|
- Each polygon is represented by a subpath. A subpath continues until it is closed by a Z command. Multiple subpaths are represented as individual elements in `#to_multipolygon`, whereas only the first subpath is represented in `#to_polygon`. Make sure your data does not include multiple subpaths if using the `#to_polygon` method.
|
182
|
+
- "-0.0" is a possible value, [as negative zero is a concept in BigDecimal](http://www.ruby-doc.org/stdlib-1.9.3/libdoc/bigdecimal/rdoc/BigDecimal.html#class-BigDecimal-label-Positive+and+negative+zero).
|
61
183
|
|
62
184
|
## Contributing
|
63
185
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
68
|
-
5. Create new Pull Request
|
186
|
+
If you have a path that you think is being mistranslated, please submit a pull request with your path added to `spec/test_cases.yml`, and if possible, the fix.
|
187
|
+
|
188
|
+
Otherwise, submit a pull request for any other feature.
|
data/changelog.md
ADDED
data/lib/wicket.rb
CHANGED
@@ -1,13 +1,35 @@
|
|
1
1
|
require "bigdecimal"
|
2
|
+
require 'bigdecimal/util'
|
2
3
|
require "wicket/version"
|
4
|
+
require "wicket/utilities"
|
5
|
+
require "wicket/cartesian"
|
6
|
+
require "wicket/configuration"
|
7
|
+
require "wicket/coordinate"
|
8
|
+
require "wicket/subpoint"
|
3
9
|
require "wicket/svg_path"
|
4
10
|
require "wicket/subpath"
|
11
|
+
require "wicket/commands/bezier_curve"
|
12
|
+
require "wicket/commands/cubic_bezier"
|
13
|
+
require "wicket/commands/quadratic_bezier"
|
5
14
|
require "wicket/command"
|
6
15
|
require "wicket/commands/m"
|
7
16
|
require "wicket/commands/l"
|
8
17
|
require "wicket/commands/z"
|
9
18
|
require "wicket/commands/h"
|
10
19
|
require "wicket/commands/v"
|
20
|
+
require "wicket/commands/c"
|
21
|
+
require "wicket/commands/s"
|
22
|
+
require "wicket/commands/q"
|
23
|
+
require "wicket/commands/t"
|
24
|
+
|
11
25
|
|
12
26
|
module Wicket
|
27
|
+
class << self
|
28
|
+
attr_reader :configuration
|
29
|
+
def configure
|
30
|
+
@configuration ||= Configuration.new
|
31
|
+
yield(@configuration)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
configure {}
|
13
35
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Wicket
|
2
|
+
module Cartesian
|
3
|
+
|
4
|
+
def distance_to(remote)
|
5
|
+
Math.sqrt((remote.x - x) ** 2 + (remote.y - y) ** 2)
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_wkt(o={})
|
9
|
+
[format(x,o),format((y * -1),o)].join(" ")
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_svg(o={})
|
13
|
+
[x,y].map{|a| format(a,o)}.join(",")
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def format(n,opts={})
|
19
|
+
Utilities.format(n,opts)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
data/lib/wicket/command.rb
CHANGED
@@ -1,45 +1,78 @@
|
|
1
1
|
module Wicket
|
2
2
|
class Command
|
3
|
-
attr_reader :cursor_start
|
3
|
+
attr_reader :cursor_start, :x, :y
|
4
4
|
|
5
5
|
class << self
|
6
|
-
def from_code(code,arg_string,subpath)
|
6
|
+
def from_code(code,arg_string,subpath,opts={})
|
7
7
|
command_class = Commands.const_get(code.upcase) # get the class which handles this code
|
8
8
|
absolute = (code.upcase == code) # is it uppercase?
|
9
9
|
cursor_start = subpath.cursor_end # get the current position of the cursor
|
10
10
|
args = arg_string.to_s.scan(/(?:\-\s*)?\d+(?:\.\d+)?/).flatten # parse out the numerical arguments
|
11
11
|
if !args.empty?
|
12
|
-
|
12
|
+
set_scale!(args,opts)
|
13
|
+
generate_commands(args,command_class,absolute,cursor_start,subpath,opts)
|
13
14
|
else # Must be a Z command
|
14
|
-
[command_class.new(absolute,cursor_start)]
|
15
|
+
[command_class.new(absolute,cursor_start,subpath,opts)]
|
15
16
|
end
|
16
17
|
end
|
17
18
|
|
18
19
|
private
|
19
20
|
|
20
|
-
def generate_commands(args,command_class,absolute,cursor_start)
|
21
|
-
args.each_slice(command_class
|
21
|
+
def generate_commands(args,command_class,absolute,cursor_start,subpath,opts={})
|
22
|
+
args.each_slice(command_class::ARGS).map do |slice| # slice them according to the number the code takes
|
22
23
|
slice.map!{|arg| BigDecimal.new(arg.gsub(/\s*/,'')) } # remove whitespace and turn into a decimal
|
23
|
-
command_class.new(absolute,cursor_start,*slice)
|
24
|
+
command_class.new(absolute,cursor_start,subpath,opts,*slice)
|
24
25
|
end
|
25
26
|
end
|
27
|
+
|
28
|
+
def set_scale!(args,opts={})
|
29
|
+
opts[:_scale] = [opts[:_scale].to_i,args.map{|s| d = s.split(".")[1]; d ? d.length : 0 }.max].max
|
30
|
+
end
|
26
31
|
end
|
27
32
|
|
28
33
|
def subpath=(subpath)
|
29
34
|
@subpath = subpath
|
30
35
|
end
|
31
36
|
|
32
|
-
def
|
33
|
-
|
37
|
+
def absolute_x
|
38
|
+
@absolute ? x : cursor_start.x + ( @x || 0 )
|
34
39
|
end
|
35
40
|
|
36
|
-
def
|
37
|
-
|
41
|
+
def absolute_y
|
42
|
+
@absolute ? y : cursor_start.y + ( @y || 0 )
|
38
43
|
end
|
39
44
|
|
40
|
-
|
41
|
-
|
42
|
-
|
45
|
+
def cursor_end
|
46
|
+
Coordinate.new(absolute_x,absolute_y)
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_wkt(opts={})
|
50
|
+
o = @opts.merge(opts)
|
51
|
+
cursor_end.to_wkt(o)
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_svg(opts={})
|
55
|
+
o = @opts.merge(opts)
|
56
|
+
[letter,cursor_end.to_svg(o)].join("")
|
57
|
+
end
|
58
|
+
|
59
|
+
def arguments
|
60
|
+
self.class::ARG_LIST.each_with_object({},&arg_maker)
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def letter
|
66
|
+
self.class.to_s.match(/\:\:([^\:]+)$/)[1]
|
67
|
+
end
|
68
|
+
|
69
|
+
def arg_maker
|
70
|
+
Proc.new do |att,hash|
|
71
|
+
hash.merge!(att => instance_variable_get("@#{att}"))
|
43
72
|
end
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
|
44
77
|
end
|
45
78
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Wicket
|
2
|
+
module Commands
|
3
|
+
module BezierCurve
|
4
|
+
|
5
|
+
def evaluate_curve(t)
|
6
|
+
x = de_casteljau(:x,t,*control_points)
|
7
|
+
y = de_casteljau(:y,t,*control_points)
|
8
|
+
[x,y,t]
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_wkt(opts={})
|
12
|
+
o = @opts.merge(opts)
|
13
|
+
subpoints(o).reject{|s| s.t == 0 }.map{|s| s.to_wkt(o) }.join(",")
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_svg(opts={})
|
17
|
+
o = @opts.merge(opts)
|
18
|
+
"L #{subpoints(o).reject{|s| s.t == 0}.map{|s| s.to_svg(o) }.join(" ")}"
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def subpoints(opts)
|
24
|
+
points = init_subpoints
|
25
|
+
smooth(points,new_point(points,*points),opts)
|
26
|
+
points.sort_by(&:t)
|
27
|
+
end
|
28
|
+
|
29
|
+
def de_casteljau(axis,t,*points)
|
30
|
+
n = (points.length - 1)
|
31
|
+
points.each_with_index.inject(0) do |m,(p,i)|
|
32
|
+
m + ( choose(n,i) * ((1-t) ** (n-i)) * (t ** i) * p.send(axis))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def choose(n,k)
|
37
|
+
(0...k).inject(1){ |m,i| (m * (n - i)) / (i + 1) }.to_d
|
38
|
+
end
|
39
|
+
|
40
|
+
def smooth(points,point,opts)
|
41
|
+
if point_needed?(point,opts)
|
42
|
+
smooth(points,new_point(points,point,point.previous_neighbor),opts)
|
43
|
+
if point_needed?(point,opts)
|
44
|
+
smooth(points,new_point(points,point,point.next_neighbor),opts)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# is the angle more than the tolerance?
|
50
|
+
def point_needed?(point,opts)
|
51
|
+
point.angle < Utilities.radians_tolerance(opts)
|
52
|
+
end
|
53
|
+
|
54
|
+
def new_point(points,point,neighbor)
|
55
|
+
x,y,t = evaluate_curve((point.t + neighbor.t) / 2.0)
|
56
|
+
Subpoint.new(x,y,t,points)
|
57
|
+
end
|
58
|
+
|
59
|
+
def init_subpoints
|
60
|
+
[].tap do |a|
|
61
|
+
Subpoint.from_coordinate(@cursor_start,0,a)
|
62
|
+
Subpoint.from_coordinate(cursor_end,1,a)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def set_implicit_control_point!
|
67
|
+
if @absolute
|
68
|
+
@c1x,@c1y = implied_c1.x,implied_c1.y
|
69
|
+
else
|
70
|
+
@c1x,@c1y = @cursor_start.relativize(implied_c1)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|