xrvg 0.0.3 → 0.0.4
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.
- data/CHANGES +21 -0
- data/README +3 -3
- data/Rakefile +4 -4
- data/examples/bezierbasic.rb +1 -0
- data/examples/bezierbasicvector.rb +1 -0
- data/examples/foreach.rb +1 -0
- data/examples/geodash.rb +1 -0
- data/examples/geodash2.rb +1 -0
- data/examples/hellocrown.rb +1 -0
- data/examples/hellocrown2.rb +1 -0
- data/examples/hellocrownrecurse.rb +1 -0
- data/examples/helloworldcompact.rb +1 -0
- data/examples/helloworldexpanded.rb +1 -0
- data/examples/multibezierbasic.rb +1 -0
- data/examples/palette_circle.rb +1 -0
- data/examples/randomdash.rb +1 -0
- data/examples/range_examples.rb +1 -0
- data/examples/range_examples2.rb +1 -0
- data/examples/sample.rb +1 -0
- data/examples/simpledash.rb +1 -0
- data/examples/uplets.rb +1 -0
- data/lib/bezier.rb +21 -55
- data/lib/bezierbuilders.rb +194 -0
- data/lib/beziermotifs.rb +114 -0
- data/lib/bezierspline.rb +20 -75
- data/lib/beziertools.rb +211 -0
- data/lib/color.rb +26 -7
- data/lib/fitting.rb +203 -0
- data/lib/frame.rb +2 -1
- data/lib/geometry2D.rb +6 -5
- data/lib/interbezier.rb +87 -0
- data/lib/interpolation.rb +6 -5
- data/lib/parametriclength.rb +87 -0
- data/lib/render.rb +4 -9
- data/lib/samplation.rb +71 -82
- data/lib/shape.rb +47 -25
- data/lib/style.rb +2 -1
- data/lib/trace.rb +2 -0
- data/lib/utils.rb +111 -17
- data/lib/xrvg.rb +16 -6
- data/test/test_attributable.rb +34 -2
- data/test/test_bezier.rb +93 -2
- data/test/test_bezierbuilders.rb +92 -0
- data/test/test_beziertools.rb +97 -0
- data/test/test_color.rb +65 -24
- data/test/test_fitting.rb +47 -0
- data/test/test_frame.rb +7 -2
- data/test/test_geometry2D.rb +26 -7
- data/test/test_interbezier.rb +29 -0
- data/test/test_interpolation.rb +16 -1
- data/test/test_parametric_length.rb +15 -0
- data/test/test_render.rb +54 -6
- data/test/test_shape.rb +103 -10
- data/test/test_trace.rb +13 -0
- data/test/test_utils.rb +114 -12
- data/test/test_xrvg.rb +3 -0
- metadata +16 -5
- data/lib/assertion.rb +0 -14
- data/lib/attributable.rb +0 -152
data/lib/beziermotifs.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
# See
|
2
|
+
# - BezierMotif
|
3
|
+
# - PicBezier
|
4
|
+
# - ArcBezier
|
5
|
+
# - LinearBezier
|
6
|
+
|
7
|
+
require 'bezierbuilders'
|
8
|
+
|
9
|
+
module XRVG
|
10
|
+
|
11
|
+
# = BezierMotif class
|
12
|
+
# == Content
|
13
|
+
# Abstract class to define prototype of a motif bezier factory
|
14
|
+
# Motif is localized by the :support attribute
|
15
|
+
# == Attributes
|
16
|
+
# attribute :support
|
17
|
+
class BezierMotif < BezierBuilder
|
18
|
+
include Attributable
|
19
|
+
attribute :support
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
# = PicBezier class
|
25
|
+
# == Content
|
26
|
+
# Build a curved "pic" bezier whose base is specified by two points, and whose shape is controlled by two attributes, :height and :curvature.
|
27
|
+
# :height parameter is a factor of base length
|
28
|
+
# == Attributes
|
29
|
+
# attribute :height, 1.0
|
30
|
+
# attribute :curvature, 1.0
|
31
|
+
# == Example
|
32
|
+
# pic = PicBezier[ :support, [V2D::O, V2D::X], :height, 1.0, :curvature, 2.0 ]
|
33
|
+
class PicBezier < BezierMotif
|
34
|
+
attribute :height, 1.0
|
35
|
+
attribute :curvature, 1.0
|
36
|
+
|
37
|
+
# BezierBuilder compute overloading.
|
38
|
+
#
|
39
|
+
# See code for algorithm
|
40
|
+
def compute
|
41
|
+
p1, p2 = self.support
|
42
|
+
onethird = (1.0 / 3.0)
|
43
|
+
p1top2 = p2 - p1
|
44
|
+
pp1 = p1 + p1top2.ortho.*(@height)
|
45
|
+
pp2 = pp1 + ( p1top2 * @curvature )
|
46
|
+
|
47
|
+
p1topp2 = pp2 - p1
|
48
|
+
pc1 = p1 + ( p1topp2 * onethird )
|
49
|
+
pc2 = p2 + ( p1topp2 * onethird )
|
50
|
+
|
51
|
+
p3 = p1 + ( p1top2 * @curvature )
|
52
|
+
p4 = p1 + ( p1top2 * ( @curvature + 1.0 ) )
|
53
|
+
|
54
|
+
pp1top2 = p3 - pp1
|
55
|
+
pp1top3 = p4 - pp1
|
56
|
+
|
57
|
+
pp1c1 = pp1 + ( pp1top2 * onethird )
|
58
|
+
pp1c2 = pp1 + ( pp1top3 * onethird )
|
59
|
+
|
60
|
+
return [[:raw, p1, pc1, pp1c1, pp1], [:raw, pp1, pp1c2, pc2, p2]]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# = ArcBezier class
|
65
|
+
# == Content
|
66
|
+
# Build an "arc" bezier whose base is specified by two points, and whose shape is controlled by a :height attribute.
|
67
|
+
# :height parameter is a factor of base length
|
68
|
+
# == Attributes
|
69
|
+
# attribute :height, 1.0
|
70
|
+
# == Example
|
71
|
+
# arc = ArcBezier[ :support, [V2D::O, V2D::X], :height, 1.0 ]
|
72
|
+
class ArcBezier < BezierMotif
|
73
|
+
attribute :height, 1.0
|
74
|
+
|
75
|
+
# BezierBuilder compute overloading.
|
76
|
+
#
|
77
|
+
# See code for algorithm
|
78
|
+
def compute
|
79
|
+
p1, p2 = self.support
|
80
|
+
v = (p2 - p1).ortho * @height
|
81
|
+
return [[:vector, p1, v, p2, v]]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# = LinearBezier class
|
86
|
+
# == Content
|
87
|
+
# Build an line bezier whose base is specified by two points.
|
88
|
+
# == Attributes
|
89
|
+
# None, apart from :support
|
90
|
+
# == Example
|
91
|
+
# line = LinearBezier[ :support, [V2D::O, V2D::X] ]
|
92
|
+
class LinearBezier < BezierMotif
|
93
|
+
attribute :support, [V2D::O, V2D::X]
|
94
|
+
|
95
|
+
# BezierBuilder compute overloading.
|
96
|
+
#
|
97
|
+
# See code for algorithm
|
98
|
+
def compute
|
99
|
+
p1, p2 = self.support
|
100
|
+
return [[:vector, p1, (p2-p1) * 1.0 / 3.0, p2, (p1 - p2) * 1.0 / 3.0]]
|
101
|
+
end
|
102
|
+
|
103
|
+
# Utilitary method to build a unit bezier line with angle
|
104
|
+
def LinearBezier.buildwithangle( angle )
|
105
|
+
return LinearBezier[ :support, [V2D::O, V2D::X.rotate( angle )]]
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
end # end XRVG
|
111
|
+
|
112
|
+
|
113
|
+
|
114
|
+
|
data/lib/bezierspline.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
# +BezierSpline+ source
|
2
2
|
|
3
3
|
require 'interpolation'
|
4
|
+
require 'parametriclength'
|
4
5
|
|
5
|
-
|
6
|
+
module XRVG
|
6
7
|
# BezierSpline class
|
7
8
|
#
|
8
9
|
# Internal class to represent a single-piece cubic bezier curve, defined by four points or two point + two vectors.
|
@@ -42,7 +43,7 @@ class BezierSpline #:nodoc:
|
|
42
43
|
@vectorpointlist = nil
|
43
44
|
if type == :raw
|
44
45
|
@rawpointlist = [v1, v2, v3, v4]
|
45
|
-
|
46
|
+
else
|
46
47
|
@vectorpointlist = [v1, v2, v3, v4]
|
47
48
|
end
|
48
49
|
end
|
@@ -73,16 +74,25 @@ class BezierSpline #:nodoc:
|
|
73
74
|
end
|
74
75
|
end
|
75
76
|
|
76
|
-
# shortcut method to get
|
77
|
+
# shortcut method to get piece first point
|
77
78
|
def firstpoint
|
78
79
|
return self.pointlist()[0]
|
79
80
|
end
|
80
81
|
|
81
|
-
# shortcut method to get
|
82
|
+
# shortcut method to get piece last point
|
82
83
|
def lastpoint
|
83
84
|
return self.pointlist()[-1]
|
84
85
|
end
|
85
86
|
|
87
|
+
# shortcut method to get piece first vector
|
88
|
+
def firstvector
|
89
|
+
return self.pointlist(:vector)[1]
|
90
|
+
end
|
91
|
+
|
92
|
+
# shortcut method to get piece last vector
|
93
|
+
def lastvector
|
94
|
+
return self.pointlist(:vector)[-1]
|
95
|
+
end
|
86
96
|
|
87
97
|
# -------------------------------------------------------------
|
88
98
|
# bezier formula
|
@@ -191,78 +201,13 @@ class BezierSpline #:nodoc:
|
|
191
201
|
render.add( Line[ :points, [p2, pc2] ], Style[ :stroke, "red", :strokewidth, (r2 / 10.0) ])
|
192
202
|
end
|
193
203
|
|
194
|
-
#
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
# compute the length of the bezier curve defined by the points
|
200
|
-
#
|
201
|
-
# Algo : recursively approximate curve by lines, until length variation is under some epsilon
|
202
|
-
#
|
203
|
-
# for the moment, just take a fix number of samples, and some it
|
204
|
-
def compute_length_interpolator() #:nodoc:
|
205
|
-
sum = 0.0
|
206
|
-
previous = nil
|
207
|
-
samplelist = [0.0, 0.0]
|
208
|
-
new = V2D[0.0,0.0]
|
209
|
-
previous = nil
|
210
|
-
(0.0..1.0).samples( 129 ) do |abs|
|
211
|
-
self.point( abs, new )
|
212
|
-
if previous
|
213
|
-
sum+= (new - previous).r
|
214
|
-
samplelist += [sum, abs]
|
215
|
-
else
|
216
|
-
previous = V2D[0.0,0.0]
|
217
|
-
end
|
218
|
-
previous.x = new.x
|
219
|
-
previous.y = new.y
|
220
|
-
end
|
221
|
-
@length = samplelist[-2]
|
222
|
-
|
223
|
-
length_interpolator = nil
|
224
|
-
if @length == 0.0
|
225
|
-
newsamplelist = [0.0,0.0,0.0,1.0]
|
226
|
-
invsamplelist = [0.0,0.0,1.0,0.0]
|
227
|
-
else
|
228
|
-
newsamplelist = []
|
229
|
-
invsamplelist = []
|
230
|
-
samplelist.foreach do |sum, abs|
|
231
|
-
newsamplelist += [sum / @length, abs ]
|
232
|
-
invsamplelist += [abs, sum / @length ]
|
233
|
-
end
|
234
|
-
samplelist = newsamplelist
|
235
|
-
end
|
236
|
-
@abs_interpolator = InterpolatorQuad.new( :samplelist, invsamplelist )
|
237
|
-
return InterpolatorQuad.new( :samplelist, samplelist )
|
238
|
-
end
|
239
|
-
|
240
|
-
def length_interpolator() #:nodoc:
|
241
|
-
if @length_interpolator == nil
|
242
|
-
@length_interpolator = self.compute_length_interpolator()
|
243
|
-
end
|
244
|
-
return @length_interpolator
|
245
|
-
end
|
246
|
-
|
247
|
-
def length(t=nil)
|
248
|
-
if @length == nil
|
249
|
-
self.compute_length()
|
250
|
-
end
|
251
|
-
if not t
|
252
|
-
return @length
|
253
|
-
else
|
254
|
-
return @abs_interpolator.interpolate( t )
|
255
|
-
end
|
204
|
+
# length computation
|
205
|
+
include ParametricLength
|
206
|
+
def parameter_range
|
207
|
+
return (0.0..1.0)
|
256
208
|
end
|
257
209
|
|
258
|
-
|
259
|
-
self.length_interpolator()
|
260
|
-
return @length
|
261
|
-
end
|
210
|
+
alias pointfromparameter point
|
262
211
|
|
263
|
-
def parameterfromlength( lvalue ) #:nodoc:
|
264
|
-
result = self.length_interpolator.interpolate( lvalue )
|
265
|
-
return result
|
266
|
-
end
|
267
212
|
end
|
268
|
-
|
213
|
+
end
|
data/lib/beziertools.rb
ADDED
@@ -0,0 +1,211 @@
|
|
1
|
+
# Some BezierBuilder implementations
|
2
|
+
# - old-fashioned SimpleBezier
|
3
|
+
# - useful BezierLevel
|
4
|
+
# - powerful Offset
|
5
|
+
# - interesting Ondulation
|
6
|
+
# - simple ClosureBezier
|
7
|
+
|
8
|
+
require 'bezierbuilders'
|
9
|
+
|
10
|
+
module XRVG
|
11
|
+
# = SimpleBezier
|
12
|
+
# == Content
|
13
|
+
# Simple Bezier interpolator, that builds a multipiece "regular" bezier curve from a list of points
|
14
|
+
# == Algo
|
15
|
+
# For each point triplet :
|
16
|
+
# - tangent vector of the middle point is the vector mean of vector from first to middle and vector frommiddle to last.
|
17
|
+
# First and last tangents are computed by symetry
|
18
|
+
# == Note
|
19
|
+
# FittingBezier is a better class for point bezier interpolation. However, this class is kept mainly for historical reasons.
|
20
|
+
class SimpleBezier < BezierBuilder
|
21
|
+
attribute :support, nil, Array
|
22
|
+
|
23
|
+
# BezierBuilder overloading: see SimpleBezier description for algorithm
|
24
|
+
def compute( )
|
25
|
+
points = @support
|
26
|
+
if points.length < 2
|
27
|
+
Kernel::raise("SimpleBezier support must have at least two points")
|
28
|
+
elsif points.length == 2
|
29
|
+
return LinearBezier.build( :support, points).data
|
30
|
+
end
|
31
|
+
|
32
|
+
result = Array.new
|
33
|
+
p1 = points[0]
|
34
|
+
v1 = V2D::O
|
35
|
+
cpiece = [:vector, p1, v1]
|
36
|
+
|
37
|
+
points.triplets do |p1, p2, p3|
|
38
|
+
v = ((p2 - p1)..( p3 - p2 )).middle * 1.0 / 3.0
|
39
|
+
result.push( cpiece + [p2, v.reverse] )
|
40
|
+
cpiece = [:vector, p2, v]
|
41
|
+
end
|
42
|
+
|
43
|
+
pr2 = points[-1]
|
44
|
+
vr2 = V2D::O
|
45
|
+
result.push( cpiece + [pr2, vr2] )
|
46
|
+
|
47
|
+
# compute first and last piece again, by symetry
|
48
|
+
piece0 = Bezier.single( *result[0] )
|
49
|
+
p1, v1, p2, v2 = piece0.pointlist(:vector)
|
50
|
+
pv = (p2 - p1)
|
51
|
+
angle = v2.angle - pv.angle
|
52
|
+
v1 = (-v2).rotate( -2.0 * angle )
|
53
|
+
result[0] = [:vector, p1, v1, p2, v2]
|
54
|
+
|
55
|
+
piecel = Bezier.single( *result[-1] )
|
56
|
+
p1, v1, p2, v2 = piecel.pointlist(:vector)
|
57
|
+
pv = (p2 - p1)
|
58
|
+
angle = v1.angle - pv.angle
|
59
|
+
v2 = (-v1).rotate( -2.0 * angle )
|
60
|
+
result[-1] = [:vector, p1, v1, p2, v2]
|
61
|
+
|
62
|
+
return result
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
# = Offset bezier builder
|
68
|
+
# == Content
|
69
|
+
# Generic offset bezier builder.
|
70
|
+
# == Attributes
|
71
|
+
# attribute :support, nil, Curve
|
72
|
+
# attribute :abscissasampler, (0.0..1.0), Samplable
|
73
|
+
# attribute :ampl, 0.5, :samplable
|
74
|
+
# attribute :nsamples, 100
|
75
|
+
class Offset < FitBezierBuilder
|
76
|
+
attribute :support, nil, Curve
|
77
|
+
attribute :abscissasampler, (0.0..1.0), Samplable
|
78
|
+
attribute :ampl, 0.5, :samplable
|
79
|
+
attribute :nsamples, 100
|
80
|
+
|
81
|
+
# overload FitBezierBuilder.points to compute Offset points
|
82
|
+
#
|
83
|
+
# Algo: for each sample, compute point, normal and amp, and newpoint = point + normal.norm * ampl
|
84
|
+
def points
|
85
|
+
result = []
|
86
|
+
[self.abscissasampler, self.ampl].samples( self.nsamples) do |abscissa, amplsample|
|
87
|
+
frame = self.support.frame( abscissa )
|
88
|
+
result << frame.center + frame.vector.ortho.norm * amplsample
|
89
|
+
end
|
90
|
+
return result
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# = Fuseau bezier builder
|
95
|
+
# == Content
|
96
|
+
# Just shortcut class for Offset with :ampl = (1.0..0.0)
|
97
|
+
# == Attributes
|
98
|
+
# attribute :maxwidth, 0.1
|
99
|
+
class Fuseau < Offset
|
100
|
+
attribute :maxwidth, 0.1
|
101
|
+
|
102
|
+
# overload Offset.ampl method by returning (self.maxwidth..0.0)
|
103
|
+
def ampl
|
104
|
+
return (self.maxwidth..0.0)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
|
110
|
+
# = BezierLevel bezier builder
|
111
|
+
# == Content
|
112
|
+
# Compute "roller coaster" bezier curves
|
113
|
+
#
|
114
|
+
# Can be used as a x-progressing curve, that is as an interpolation curve
|
115
|
+
# == Attributes
|
116
|
+
# attribute :samplelist, [], Array
|
117
|
+
# :samplelist must contain pairs of cartesien coords [x1,y1,x2,y2,...], x between 0.0 and 1.0 (as for interpolator)
|
118
|
+
class BezierLevel < BezierBuilder
|
119
|
+
attribute :samplelist, [], Array
|
120
|
+
|
121
|
+
# Overload BezierBuilder build method
|
122
|
+
#
|
123
|
+
# Algo: simply interpolate [x,y] couples as V2D, with SimpleBezier bezier builder
|
124
|
+
def BezierLevel.build( *args )
|
125
|
+
builder = BezierLevel.new( *args )
|
126
|
+
points = []
|
127
|
+
builder.samplelist.foreach do |x,y|
|
128
|
+
points << V2D[x,y]
|
129
|
+
end
|
130
|
+
return SimpleBezier[ :support, points ]
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# = ClosureBezier bezier builder
|
135
|
+
# == Content
|
136
|
+
# Simple bezier operator that take a list of beziers and produce a concatenate multipieces closed bezier curve.
|
137
|
+
# Missing segments are completed with lines
|
138
|
+
class ClosureBezier < BezierBuilder
|
139
|
+
attribute :bezierlist
|
140
|
+
|
141
|
+
# BezierBuilder compute overloading
|
142
|
+
def compute
|
143
|
+
result = []
|
144
|
+
result += self.bezierlist[0].pieces
|
145
|
+
self.bezierlist[1..-1].each do |bezier|
|
146
|
+
lastpoint = result[-1].lastpoint
|
147
|
+
newpoint = bezier.firstpoint
|
148
|
+
if not V2D.vequal?( lastpoint, newpoint )
|
149
|
+
result += LinearBezier[ :support, [lastpoint, newpoint]].pieces
|
150
|
+
end
|
151
|
+
result += bezier.pieces
|
152
|
+
end
|
153
|
+
lastpoint = result[-1].lastpoint
|
154
|
+
newpoint = result[0].firstpoint
|
155
|
+
if not V2D.vequal?( lastpoint, newpoint )
|
156
|
+
result += LinearBezier[ :support, [lastpoint, newpoint]].pieces
|
157
|
+
end
|
158
|
+
result = result.map {|piece| piece.data}
|
159
|
+
# Trace("result #{result.inspect}")
|
160
|
+
return result
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# = Ondulation bezier builder
|
165
|
+
# == Content
|
166
|
+
# Generic ondulation bezier builder.
|
167
|
+
# == Attributes
|
168
|
+
# attribute :support, nil, Curve
|
169
|
+
# attribute :ampl, 0.5, :samplable
|
170
|
+
# attribute :abscissasampler, (0.0..1.0), Samplable
|
171
|
+
# attribute :freq, 10
|
172
|
+
# :support is a Curve
|
173
|
+
# :abscissas must be a Float Samplable, as (0.0..1.0).geo(3.0)
|
174
|
+
# :ampl can be a constant or a sampler
|
175
|
+
# :freq is the number of oscillations to be computed
|
176
|
+
class Ondulation < BezierBuilder
|
177
|
+
attribute :support, nil, Curve
|
178
|
+
attribute :ampl, 0.5, :samplable
|
179
|
+
attribute :abscissasampler, (0.0..1.0), Samplable
|
180
|
+
attribute :freq, 10
|
181
|
+
|
182
|
+
|
183
|
+
# algo : for each abscissa, 0.0 of the curve (given the normal)
|
184
|
+
# and for each mean abscissa, :amp normal
|
185
|
+
def compute
|
186
|
+
abscissas = self.abscissasampler.samples( self.freq + 1 )
|
187
|
+
sens = 1.0
|
188
|
+
pieces = []
|
189
|
+
[abscissas.pairs, self.ampl.samples( self.freq )].forzip do |abspair, amplitude|
|
190
|
+
abs1, abs2 = abspair
|
191
|
+
mabs = (abs1 + abs2)/2.0
|
192
|
+
p1, halfpoint, p2 = self.support.points( [abs1, mabs, abs2] )
|
193
|
+
# Trace("mabs #{mabs} abs1 #{abs1} abs2 #{abs2} halfpoint #{halfpoint.inspect} p1 #{p1.inspect} p2 #{p2.inspect}")
|
194
|
+
# Trace("normal #{@support.normal( mabs )}")
|
195
|
+
halfnormal = self.support.normal( mabs ).norm * ( sens * amplitude * (p2 - p1).length)
|
196
|
+
# Trace("halfnormal #{halfnormal.inspect}")
|
197
|
+
newpoint = halfpoint + halfnormal
|
198
|
+
tpoint = halfpoint + halfnormal * 3.0
|
199
|
+
t1 = (tpoint - p1 ) / 6.0
|
200
|
+
t2 = (tpoint - p2 ) / 6.0
|
201
|
+
# Trace("newpoint #{newpoint.inspect} p1 #{p1.inspect} (newpoint - p1) #{(newpoint - p1).inspect}")
|
202
|
+
halftangent = self.support.tangent( mabs ).norm * (newpoint - p1).length / 3.0
|
203
|
+
# halftangent = self.support.tangent( mabs ).norm * (p2 - p1).length / 3.0
|
204
|
+
pieces += [[:vector, p1, t1, newpoint, -halftangent], [:vector, newpoint, halftangent, p2, t2]]
|
205
|
+
sens *= -1.0
|
206
|
+
end
|
207
|
+
return pieces
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
end # XRVG
|