chipmunk 5.3.4.0 → 5.3.4.2
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/README +36 -13
- data/Rakefile +23 -9
- data/ext/chipmunk/extconf.rb +55 -12
- data/ext/chipmunk/rb_chipmunk.c +92 -98
- data/ext/chipmunk/rb_chipmunk.h +44 -34
- data/ext/chipmunk/rb_cpArbiter.c +135 -98
- data/ext/chipmunk/rb_cpBB.c +84 -101
- data/ext/chipmunk/rb_cpBody.c +221 -243
- data/ext/chipmunk/rb_cpConstraint.c +173 -185
- data/ext/chipmunk/rb_cpShape.c +353 -240
- data/ext/chipmunk/rb_cpSpace.c +376 -408
- data/ext/chipmunk/rb_cpVect.c +135 -173
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/chipmunk.h +163 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/chipmunk_ffi.h +59 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/chipmunk_private.h +49 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/chipmunk_types.h +151 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/chipmunk_unsafe.h +54 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpConstraint.h +105 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpDampedRotarySpring.h +46 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpDampedSpring.h +53 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpGearJoint.h +41 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpGrooveJoint.h +48 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpPinJoint.h +43 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpPivotJoint.h +42 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpRatchetJoint.h +40 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpRotaryLimitJoint.h +39 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpSimpleMotor.h +37 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpSlideJoint.h +44 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/util.h +134 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpArbiter.h +188 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpArray.h +49 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpBB.h +74 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpBody.h +219 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpCollision.h +28 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpHashSet.h +82 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpPolyShape.h +103 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpShape.h +177 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpSpace.h +206 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpSpaceHash.h +110 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpVect.h +207 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/chipmunk.c +151 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpConstraint.c +54 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpDampedRotarySpring.c +105 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpDampedSpring.c +115 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpGearJoint.c +113 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpGrooveJoint.c +161 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpPinJoint.c +116 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpPivotJoint.c +114 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpRatchetJoint.c +126 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpRotaryLimitJoint.c +120 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpSimpleMotor.c +97 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpSlideJoint.c +129 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpArbiter.c +280 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpArray.c +143 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpBB.c +47 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpBody.c +192 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpCollision.c +411 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpHashSet.c +253 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpPolyShape.c +240 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpShape.c +405 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpSpace.c +499 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpSpaceComponent.c +279 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpSpaceHash.c +534 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpSpaceQuery.c +246 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpSpaceStep.c +398 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpVect.c +71 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/prime.h +68 -0
- data/lib/chipmunk.rb +19 -8
- metadata +85 -38
data/ext/chipmunk/rb_cpShape.c
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/* Copyright (c) 2007 Scott Lembcke
|
|
2
|
-
*
|
|
2
|
+
*
|
|
3
3
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
4
|
* of this software and associated documentation files (the "Software"), to deal
|
|
5
5
|
* in the Software without restriction, including without limitation the rights
|
|
6
6
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
7
|
* copies of the Software, and to permit persons to whom the Software is
|
|
8
8
|
* furnished to do so, subject to the following conditions:
|
|
9
|
-
*
|
|
9
|
+
*
|
|
10
10
|
* The above copyright notice and this permission notice shall be included in
|
|
11
11
|
* all copies or substantial portions of the Software.
|
|
12
|
-
*
|
|
12
|
+
*
|
|
13
13
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
14
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
15
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
@@ -18,9 +18,10 @@
|
|
|
18
18
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
19
|
* SOFTWARE.
|
|
20
20
|
*/
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
#include <stdlib.h>
|
|
23
23
|
#include "chipmunk.h"
|
|
24
|
+
#include "chipmunk_unsafe.h"
|
|
24
25
|
|
|
25
26
|
#include "ruby.h"
|
|
26
27
|
#include "rb_chipmunk.h"
|
|
@@ -43,175 +44,155 @@ rb_cpSegmentQueryInfoNew(VALUE shape, VALUE t, VALUE n) {
|
|
|
43
44
|
|
|
44
45
|
|
|
45
46
|
static VALUE
|
|
46
|
-
rb_cpShapeGetBody(VALUE self)
|
|
47
|
-
|
|
48
|
-
return rb_ivar_get(self, id_body);
|
|
47
|
+
rb_cpShapeGetBody(VALUE self) {
|
|
48
|
+
return rb_ivar_get(self, id_body);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
static VALUE
|
|
52
|
-
rb_cpShapeSensorP(VALUE self)
|
|
53
|
-
|
|
54
|
-
return SHAPE(self)->sensor ? Qtrue : Qfalse;
|
|
52
|
+
rb_cpShapeSensorP(VALUE self) {
|
|
53
|
+
return SHAPE(self)->sensor ? Qtrue : Qfalse;
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
static VALUE
|
|
58
|
-
rb_cpShapeSetSensor(VALUE self, VALUE sensor)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
SHAPE(self)->sensor = sens;
|
|
57
|
+
rb_cpShapeSetSensor(VALUE self, VALUE sensor) {
|
|
58
|
+
int sens = (NIL_P(sensor) || (!sensor)) ? 0 : 1;
|
|
59
|
+
SHAPE(self)->sensor = sens;
|
|
62
60
|
return sensor;
|
|
63
61
|
}
|
|
64
62
|
|
|
65
63
|
static VALUE
|
|
66
|
-
rb_cpShapeSetBody(VALUE self, VALUE body)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return body;
|
|
64
|
+
rb_cpShapeSetBody(VALUE self, VALUE body) {
|
|
65
|
+
SHAPE(self)->body = BODY(body);
|
|
66
|
+
rb_ivar_set(self, id_body, body);
|
|
67
|
+
|
|
68
|
+
return body;
|
|
72
69
|
}
|
|
73
70
|
|
|
74
71
|
static VALUE
|
|
75
|
-
rb_cpShapeGetCollType(VALUE self)
|
|
76
|
-
|
|
77
|
-
return rb_iv_get(self, "collType");
|
|
72
|
+
rb_cpShapeGetCollType(VALUE self) {
|
|
73
|
+
return rb_iv_get(self, "collType");
|
|
78
74
|
}
|
|
79
75
|
|
|
80
76
|
static VALUE
|
|
81
|
-
rb_cpShapeSetCollType(VALUE self, VALUE val)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return val;
|
|
77
|
+
rb_cpShapeSetCollType(VALUE self, VALUE val) {
|
|
78
|
+
VALUE col_type = rb_obj_id(val);
|
|
79
|
+
rb_iv_set(self, "collType", val);
|
|
80
|
+
SHAPE(self)->collision_type = NUM2UINT(col_type);
|
|
81
|
+
|
|
82
|
+
return val;
|
|
88
83
|
}
|
|
89
84
|
|
|
90
85
|
static VALUE
|
|
91
|
-
rb_cpShapeGetData(VALUE self)
|
|
92
|
-
{
|
|
86
|
+
rb_cpShapeGetData(VALUE self) {
|
|
93
87
|
return rb_iv_get(self, "data");
|
|
94
88
|
}
|
|
95
89
|
|
|
96
90
|
static VALUE
|
|
97
|
-
rb_cpShapeSetData(VALUE self, VALUE val)
|
|
98
|
-
{
|
|
91
|
+
rb_cpShapeSetData(VALUE self, VALUE val) {
|
|
99
92
|
rb_iv_set(self, "data", val);
|
|
100
93
|
return val;
|
|
101
94
|
}
|
|
102
95
|
|
|
103
96
|
static VALUE
|
|
104
|
-
rb_cpShapeGetSelf(VALUE self)
|
|
105
|
-
{
|
|
97
|
+
rb_cpShapeGetSelf(VALUE self) {
|
|
106
98
|
return self;
|
|
107
99
|
}
|
|
108
100
|
|
|
109
101
|
|
|
110
102
|
static VALUE
|
|
111
|
-
rb_cpShapeGetGroup(VALUE self)
|
|
112
|
-
|
|
113
|
-
return rb_iv_get(self, "group");
|
|
103
|
+
rb_cpShapeGetGroup(VALUE self) {
|
|
104
|
+
return rb_iv_get(self, "group");
|
|
114
105
|
}
|
|
115
106
|
|
|
116
107
|
static VALUE
|
|
117
|
-
rb_cpShapeSetGroup(VALUE self, VALUE val)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
return val;
|
|
108
|
+
rb_cpShapeSetGroup(VALUE self, VALUE val) {
|
|
109
|
+
VALUE col_type = rb_obj_id(val);
|
|
110
|
+
rb_iv_set(self, "group", val);
|
|
111
|
+
SHAPE(self)->group = NUM2UINT(col_type);
|
|
112
|
+
|
|
113
|
+
return val;
|
|
124
114
|
}
|
|
125
115
|
|
|
126
116
|
static VALUE
|
|
127
|
-
rb_cpShapeGetLayers(VALUE self)
|
|
128
|
-
|
|
129
|
-
return UINT2NUM(SHAPE(self)->layers);
|
|
117
|
+
rb_cpShapeGetLayers(VALUE self) {
|
|
118
|
+
return UINT2NUM(SHAPE(self)->layers);
|
|
130
119
|
}
|
|
131
120
|
|
|
132
121
|
static VALUE
|
|
133
|
-
rb_cpShapeSetLayers(VALUE self, VALUE layers)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
return layers;
|
|
122
|
+
rb_cpShapeSetLayers(VALUE self, VALUE layers) {
|
|
123
|
+
SHAPE(self)->layers = NUM2UINT(layers);
|
|
124
|
+
|
|
125
|
+
return layers;
|
|
138
126
|
}
|
|
139
127
|
|
|
140
128
|
static VALUE
|
|
141
|
-
rb_cpShapeGetBB(VALUE self)
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
return Data_Wrap_Struct(c_cpBB, NULL, free, bb);
|
|
129
|
+
rb_cpShapeGetBB(VALUE self) {
|
|
130
|
+
cpBB *bb = malloc(sizeof(cpBB));
|
|
131
|
+
*bb = SHAPE(self)->bb;
|
|
132
|
+
return Data_Wrap_Struct(c_cpBB, NULL, free, bb);
|
|
146
133
|
}
|
|
147
134
|
|
|
148
135
|
static VALUE
|
|
149
|
-
rb_cpShapeCacheBB(VALUE self)
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
return rb_cpShapeGetBB(self);
|
|
136
|
+
rb_cpShapeCacheBB(VALUE self) {
|
|
137
|
+
cpShape *shape = SHAPE(self);
|
|
138
|
+
cpShapeCacheBB(shape);
|
|
139
|
+
|
|
140
|
+
return rb_cpShapeGetBB(self);
|
|
155
141
|
}
|
|
156
142
|
|
|
157
143
|
static VALUE
|
|
158
|
-
rb_cpShapeGetElasticity(VALUE self)
|
|
159
|
-
|
|
160
|
-
return rb_float_new(SHAPE(self)->e);
|
|
144
|
+
rb_cpShapeGetElasticity(VALUE self) {
|
|
145
|
+
return rb_float_new(SHAPE(self)->e);
|
|
161
146
|
}
|
|
162
147
|
|
|
163
148
|
static VALUE
|
|
164
|
-
rb_cpShapeGetFriction(VALUE self)
|
|
165
|
-
|
|
166
|
-
return rb_float_new(SHAPE(self)->u);
|
|
149
|
+
rb_cpShapeGetFriction(VALUE self) {
|
|
150
|
+
return rb_float_new(SHAPE(self)->u);
|
|
167
151
|
}
|
|
168
152
|
|
|
169
153
|
static VALUE
|
|
170
|
-
rb_cpShapeSetElasticity(VALUE self, VALUE val)
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
return val;
|
|
154
|
+
rb_cpShapeSetElasticity(VALUE self, VALUE val) {
|
|
155
|
+
SHAPE(self)->e = NUM2DBL(val);
|
|
156
|
+
return val;
|
|
174
157
|
}
|
|
175
158
|
|
|
176
159
|
static VALUE
|
|
177
|
-
rb_cpShapeSetFriction(VALUE self, VALUE val)
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
return val;
|
|
160
|
+
rb_cpShapeSetFriction(VALUE self, VALUE val) {
|
|
161
|
+
SHAPE(self)->u = NUM2DBL(val);
|
|
162
|
+
return val;
|
|
181
163
|
}
|
|
182
164
|
|
|
183
165
|
static VALUE
|
|
184
|
-
rb_cpShapeGetSurfaceV(VALUE self)
|
|
185
|
-
|
|
186
|
-
return VWRAP(self, &SHAPE(self)->surface_v);
|
|
166
|
+
rb_cpShapeGetSurfaceV(VALUE self) {
|
|
167
|
+
return VWRAP(self, &SHAPE(self)->surface_v);
|
|
187
168
|
}
|
|
188
169
|
|
|
189
170
|
static VALUE
|
|
190
|
-
rb_cpShapeSetSurfaceV(VALUE self, VALUE val)
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
return val;
|
|
171
|
+
rb_cpShapeSetSurfaceV(VALUE self, VALUE val) {
|
|
172
|
+
SHAPE(self)->surface_v = *VGET(val);
|
|
173
|
+
return val;
|
|
194
174
|
}
|
|
195
175
|
|
|
196
176
|
static VALUE
|
|
197
|
-
rb_cpShapeResetIdCounter(VALUE self)
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
return Qnil;
|
|
177
|
+
rb_cpShapeResetIdCounter(VALUE self) {
|
|
178
|
+
cpResetShapeIdCounter();
|
|
179
|
+
return Qnil;
|
|
201
180
|
}
|
|
202
181
|
|
|
203
182
|
// Test if a point lies within a shape.
|
|
204
|
-
static VALUE
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
183
|
+
static VALUE
|
|
184
|
+
rb_cpShapePointQuery(VALUE self, VALUE point) {
|
|
185
|
+
cpBool res = cpShapePointQuery(SHAPE(self), *VGET(point));
|
|
186
|
+
return res ? Qtrue : Qfalse;
|
|
187
|
+
}
|
|
208
188
|
|
|
209
189
|
|
|
210
|
-
static VALUE
|
|
190
|
+
static VALUE
|
|
191
|
+
rb_cpShapeSegmentQuery(VALUE self, VALUE a, VALUE b) {
|
|
211
192
|
cpSegmentQueryInfo info;
|
|
212
193
|
cpShapeSegmentQuery(SHAPE(self), *VGET(a), *VGET(b), &info);
|
|
213
|
-
if(info.shape) {
|
|
214
|
-
return rb_cpSegmentQueryInfoNew((VALUE)info.shape->data, rb_float_new(info.t), VNEW(info.n));
|
|
194
|
+
if(info.shape) {
|
|
195
|
+
return rb_cpSegmentQueryInfoNew((VALUE)info.shape->data, rb_float_new(info.t), VNEW(info.n));
|
|
215
196
|
}
|
|
216
197
|
return Qnil;
|
|
217
198
|
}
|
|
@@ -219,215 +200,347 @@ static VALUE rb_cpShapeSegmentQuery(VALUE self, VALUE a, VALUE b) {
|
|
|
219
200
|
|
|
220
201
|
|
|
221
202
|
//cpCircle
|
|
203
|
+
static void
|
|
204
|
+
rb_cpCircleMark(void * data) {
|
|
205
|
+
cpCircleShape *circle = (cpCircleShape *) data;
|
|
206
|
+
}
|
|
207
|
+
|
|
222
208
|
static VALUE
|
|
223
|
-
rb_cpCircleAlloc(VALUE klass)
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
209
|
+
rb_cpCircleAlloc(VALUE klass) {
|
|
210
|
+
cpCircleShape *circle = cpCircleShapeAlloc();
|
|
211
|
+
// note: in chipmunk 5.3.4, there is a bug wich will cause
|
|
212
|
+
// cpShapeFree to segfault if cpXXXShapeInit has not been called.
|
|
213
|
+
return Data_Wrap_Struct(klass, NULL, cpShapeFree, circle);
|
|
227
214
|
}
|
|
228
215
|
|
|
216
|
+
|
|
217
|
+
|
|
229
218
|
static VALUE
|
|
230
|
-
rb_cpCircleInitialize(
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
circle->shape.data = (void *)self;
|
|
236
|
-
circle->shape.collision_type = Qnil;
|
|
219
|
+
rb_cpCircleInitialize(int argc, VALUE * argv, VALUE self) {
|
|
220
|
+
VALUE body, radius, offset;
|
|
221
|
+
cpCircleShape *circle = NULL;
|
|
222
|
+
rb_scan_args(argc, argv, "21", &body, &radius, &offset);
|
|
223
|
+
circle = (cpCircleShape *)SHAPE(self);
|
|
237
224
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
225
|
+
cpCircleShapeInit(circle, BODY(body), NUM2DBL(radius), VGET_ZERO(offset));
|
|
226
|
+
circle->shape.data = (void *)self;
|
|
227
|
+
circle->shape.collision_type = Qnil;
|
|
228
|
+
rb_ivar_set(self, id_body, body);
|
|
242
229
|
|
|
230
|
+
return self;
|
|
231
|
+
}
|
|
243
232
|
|
|
244
233
|
|
|
245
234
|
|
|
246
235
|
|
|
247
236
|
//cpSegment
|
|
248
237
|
static VALUE
|
|
249
|
-
rb_cpSegmentAlloc(VALUE klass)
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
238
|
+
rb_cpSegmentAlloc(VALUE klass) {
|
|
239
|
+
cpSegmentShape *seg = cpSegmentShapeAlloc();
|
|
240
|
+
|
|
241
|
+
return Data_Wrap_Struct(klass, NULL, cpShapeFree, seg);
|
|
253
242
|
}
|
|
254
243
|
|
|
255
244
|
static VALUE
|
|
256
|
-
rb_cpSegmentInitialize(VALUE self, VALUE body, VALUE a, VALUE b, VALUE r)
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
seg->shape.collision_type = Qnil;
|
|
245
|
+
rb_cpSegmentInitialize(VALUE self, VALUE body, VALUE a, VALUE b, VALUE r) {
|
|
246
|
+
cpSegmentShape *seg = (cpSegmentShape *)SHAPE(self);
|
|
247
|
+
|
|
248
|
+
cpSegmentShapeInit(seg, BODY(body), *VGET(a), *VGET(b), NUM2DBL(r));
|
|
249
|
+
seg->shape.data = (void *)self;
|
|
250
|
+
seg->shape.collision_type = Qnil;
|
|
263
251
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
252
|
+
rb_ivar_set(self, id_body, body);
|
|
253
|
+
|
|
254
|
+
return self;
|
|
267
255
|
}
|
|
268
256
|
|
|
269
257
|
|
|
270
258
|
|
|
271
259
|
//cpPoly
|
|
260
|
+
|
|
261
|
+
// Syntactic macro to handle fetching the vertices from a ruby array
|
|
262
|
+
// to a C array.
|
|
263
|
+
#define RBCP_ARRAY_POINTS(ARR, NUM, VERTS) \
|
|
264
|
+
Check_Type(ARR, T_ARRAY); \
|
|
265
|
+
VALUE *__rbcp_ptr = RARRAY_PTR(ARR); \
|
|
266
|
+
long NUM = RARRAY_LEN(ARR); \
|
|
267
|
+
cpVect VERTS[NUM]; \
|
|
268
|
+
for(long i = 0; i < NUM; i++) \
|
|
269
|
+
VERTS[i] = *VGET(__rbcp_ptr[i]);
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
static VALUE
|
|
273
|
+
rb_cpPolyValidate(VALUE self, VALUE arr) {
|
|
274
|
+
RBCP_ARRAY_POINTS(arr, num, verts);
|
|
275
|
+
|
|
276
|
+
return CP_INT_BOOL(cpPolyValidate(verts, num));
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
|
|
272
280
|
static VALUE
|
|
273
|
-
rb_cpPolyAlloc(VALUE klass)
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
return Data_Wrap_Struct(klass, NULL, cpShapeFree, poly);
|
|
281
|
+
rb_cpPolyAlloc(VALUE klass) {
|
|
282
|
+
cpPolyShape *poly = cpPolyShapeAlloc();
|
|
283
|
+
return Data_Wrap_Struct(klass, NULL, cpShapeFree, poly);
|
|
277
284
|
}
|
|
278
285
|
|
|
279
286
|
static VALUE
|
|
280
|
-
rb_cpPolyInitialize(
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
287
|
+
rb_cpPolyInitialize(int argc, VALUE * argv, VALUE self) {
|
|
288
|
+
VALUE body, arr, offset;
|
|
289
|
+
cpPolyShape *poly = (cpPolyShape *)SHAPE(self);
|
|
290
|
+
|
|
291
|
+
rb_scan_args(argc, argv, "21", &body, &arr, &offset);
|
|
292
|
+
|
|
293
|
+
RBCP_ARRAY_POINTS(arr, num, verts);
|
|
294
|
+
|
|
295
|
+
if(!cpPolyValidate(verts, num)) {
|
|
296
|
+
rb_raise(rb_eArgError, "The verts array does not from a valid polygon!");
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
cpPolyShapeInit(poly, BODY(body), num, verts, VGET_ZERO(offset));
|
|
300
|
+
poly->shape.data = (void *)self;
|
|
301
|
+
poly->shape.collision_type = Qnil;
|
|
295
302
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
303
|
+
rb_ivar_set(self, id_body, body);
|
|
304
|
+
|
|
305
|
+
return self;
|
|
299
306
|
}
|
|
300
307
|
|
|
301
308
|
// some getters
|
|
302
|
-
static VALUE
|
|
309
|
+
static VALUE
|
|
310
|
+
rb_cpCircleShapeGetOffset(VALUE self) {
|
|
303
311
|
return VNEW(cpCircleShapeGetOffset(SHAPE(self)));
|
|
304
312
|
}
|
|
305
313
|
|
|
306
|
-
static VALUE
|
|
314
|
+
static VALUE
|
|
315
|
+
rb_cpCircleShapeGetRadius(VALUE self) {
|
|
307
316
|
return rb_float_new(cpCircleShapeGetRadius(SHAPE(self)));
|
|
308
317
|
}
|
|
309
318
|
|
|
310
|
-
static VALUE
|
|
319
|
+
static VALUE
|
|
320
|
+
rb_cpSegmentShapeGetA(VALUE self) {
|
|
311
321
|
return VNEW(cpSegmentShapeGetA(SHAPE(self)));
|
|
312
322
|
}
|
|
313
323
|
|
|
314
|
-
static VALUE
|
|
324
|
+
static VALUE
|
|
325
|
+
rb_cpSegmentShapeGetB(VALUE self) {
|
|
315
326
|
return VNEW(cpSegmentShapeGetB(SHAPE(self)));
|
|
316
327
|
}
|
|
317
328
|
|
|
318
|
-
static VALUE
|
|
329
|
+
static VALUE
|
|
330
|
+
rb_cpSegmentShapeGetRadius(VALUE self) {
|
|
319
331
|
return rb_float_new(cpSegmentShapeGetRadius(SHAPE(self)));
|
|
320
332
|
}
|
|
321
333
|
|
|
322
|
-
static VALUE
|
|
334
|
+
static VALUE
|
|
335
|
+
rb_cpSegmentShapeGetNormal(VALUE self) {
|
|
323
336
|
return VNEW(cpSegmentShapeGetNormal(SHAPE(self)));
|
|
324
337
|
}
|
|
325
338
|
|
|
326
|
-
static VALUE
|
|
339
|
+
static VALUE
|
|
340
|
+
rb_cpPolyShapeGetNumVerts(VALUE self) {
|
|
327
341
|
return INT2NUM(cpPolyShapeGetNumVerts(SHAPE(self)));
|
|
328
342
|
}
|
|
329
343
|
|
|
330
|
-
static VALUE
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
344
|
+
static VALUE
|
|
345
|
+
rb_cpPolyShapeGetVert(VALUE self, VALUE vindex) {
|
|
346
|
+
cpShape *shape = SHAPE(self);
|
|
347
|
+
int index = NUM2INT(vindex);
|
|
348
|
+
if ((index < 0) || (index >= cpPolyShapeGetNumVerts(shape))) {
|
|
334
349
|
return Qnil;
|
|
335
|
-
}
|
|
350
|
+
}
|
|
336
351
|
return VNEW(cpPolyShapeGetVert(shape, index));
|
|
337
352
|
}
|
|
338
353
|
|
|
354
|
+
/* "unsafe" API. */
|
|
355
|
+
|
|
356
|
+
static VALUE
|
|
357
|
+
rb_cpCircleShapeSetRadius(VALUE self, VALUE radius) {
|
|
358
|
+
cpCircleShapeSetRadius(SHAPE(self), NUM2DBL(radius));
|
|
359
|
+
return self;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
static VALUE
|
|
363
|
+
rb_cpCircleShapeSetOffset(VALUE self, VALUE offset) {
|
|
364
|
+
cpCircleShapeSetOffset(SHAPE(self), *VGET(offset));
|
|
365
|
+
return self;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
static VALUE
|
|
369
|
+
rb_cpSegmentShapeSetEndpoints(VALUE self, VALUE a, VALUE b) {
|
|
370
|
+
cpSegmentShapeSetEndpoints(SHAPE(self), *VGET(a), *VGET(b));
|
|
371
|
+
return self;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
static VALUE
|
|
375
|
+
rb_cpSegmentShapeSetRadius(VALUE self, VALUE radius) {
|
|
376
|
+
cpSegmentShapeSetRadius(SHAPE(self), NUM2DBL(radius));
|
|
377
|
+
return self;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
static VALUE
|
|
382
|
+
rb_cpPolyShapeSetVerts(VALUE self, VALUE arr, VALUE offset) {
|
|
383
|
+
cpShape *poly = SHAPE(self);
|
|
384
|
+
|
|
385
|
+
RBCP_ARRAY_POINTS(arr, num, verts);
|
|
386
|
+
|
|
387
|
+
if(!cpPolyValidate(verts, num)) {
|
|
388
|
+
rb_raise(rb_eArgError, "The verts array does not from a valid polygon!");
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
cpPolyShapeSetVerts(poly, num, verts, *VGET(offset));
|
|
392
|
+
return self;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/* extra inline functions from the headers */
|
|
396
|
+
// Returns the minimum distance of the polygon to the axis.
|
|
397
|
+
static VALUE
|
|
398
|
+
rb_cpPolyShapeValueOnAxis(VALUE self, VALUE n, VALUE d) {
|
|
399
|
+
cpPolyShape * poly = (cpPolyShape *) SHAPE(self);
|
|
400
|
+
return DBL2NUM(cpPolyShapeValueOnAxis(poly, *VGET(n), NUM2DBL(d)));
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Returns true if the polygon contains the vertex.
|
|
404
|
+
static VALUE
|
|
405
|
+
rb_cpPolyShapeContainsVert(VALUE self, VALUE v) {
|
|
406
|
+
cpPolyShape * poly = (cpPolyShape *) SHAPE(self);
|
|
407
|
+
return CP_INT_BOOL(cpPolyShapeContainsVert(poly, *VGET(v)));
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Same as cpPolyShapeContainsVert() but ignores faces pointing away from the normal.
|
|
411
|
+
static VALUE
|
|
412
|
+
rb_cpPolyShapeContainsVertPartial(VALUE self, VALUE v, VALUE n) {
|
|
413
|
+
cpPolyShape * poly = (cpPolyShape *) SHAPE(self);
|
|
414
|
+
return CP_INT_BOOL(cpPolyShapeContainsVertPartial(poly, *VGET(v), *VGET(n)));
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
/* collision.h */
|
|
419
|
+
static VALUE
|
|
420
|
+
rb_cpCollideShapes(VALUE self, VALUE other) {
|
|
421
|
+
cpContact points[CP_MAX_CONTACTS_PER_ARBITER];
|
|
422
|
+
int size = cpCollideShapes(SHAPE(self), SHAPE(other), points);
|
|
423
|
+
VALUE result = rb_ary_new();
|
|
424
|
+
for(int index = 0; index < size; index++) {
|
|
425
|
+
VALUE point = VNEW(points[index].p);
|
|
426
|
+
VALUE normal = VNEW(points[index].n);
|
|
427
|
+
VALUE dist = DBL2NUM(points[index].dist);
|
|
428
|
+
VALUE contact = rb_cpContactPointNew(point, normal, dist);
|
|
429
|
+
rb_ary_push(result, contact);
|
|
430
|
+
}
|
|
431
|
+
return result;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
|
|
339
439
|
|
|
340
440
|
void
|
|
341
|
-
Init_cpShape(void)
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
441
|
+
Init_cpShape(void) {
|
|
442
|
+
id_body = rb_intern("body");
|
|
443
|
+
|
|
444
|
+
m_cpShape = rb_define_module_under(m_Chipmunk, "Shape");
|
|
445
|
+
rb_define_attr(m_cpShape, "obj", 1, 1);
|
|
446
|
+
|
|
447
|
+
rb_define_method(m_cpShape, "body", rb_cpShapeGetBody, 0);
|
|
448
|
+
rb_define_method(m_cpShape, "body=", rb_cpShapeSetBody, 1);
|
|
449
|
+
|
|
351
450
|
rb_define_method(m_cpShape, "sensor?", rb_cpShapeSensorP, 0);
|
|
352
451
|
rb_define_method(m_cpShape, "sensor=", rb_cpShapeSetSensor, 1);
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
// this method only exists for Chipmunk-FFI compatibility as it seems useless
|
|
359
|
-
// to me.
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
rb_define_method(m_cpShape, "collision_type", rb_cpShapeGetCollType, 0);
|
|
455
|
+
rb_define_method(m_cpShape, "collision_type=", rb_cpShapeSetCollType, 1);
|
|
456
|
+
|
|
457
|
+
// this method only exists for Chipmunk-FFI compatibility as it seems useless
|
|
458
|
+
// to me.
|
|
360
459
|
rb_define_method(m_cpShape, "data", rb_cpShapeGetSelf, 0);
|
|
361
460
|
// So we use this as the object setter
|
|
362
|
-
rb_define_method(m_cpShape, "object"
|
|
461
|
+
rb_define_method(m_cpShape, "object", rb_cpShapeGetData, 0);
|
|
363
462
|
rb_define_method(m_cpShape, "object=", rb_cpShapeSetData, 1);
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
rb_define_method(m_cpShape, "raw_bb"
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
463
|
+
|
|
464
|
+
rb_define_method(m_cpShape, "group", rb_cpShapeGetGroup, 0);
|
|
465
|
+
rb_define_method(m_cpShape, "group=", rb_cpShapeSetGroup, 1);
|
|
466
|
+
|
|
467
|
+
rb_define_method(m_cpShape, "layers", rb_cpShapeGetLayers, 0);
|
|
468
|
+
rb_define_method(m_cpShape, "layers=", rb_cpShapeSetLayers, 1);
|
|
469
|
+
|
|
470
|
+
rb_define_method(m_cpShape, "bb", rb_cpShapeCacheBB, 0);
|
|
471
|
+
rb_define_method(m_cpShape, "cache_bb", rb_cpShapeCacheBB, 0);
|
|
472
|
+
rb_define_method(m_cpShape, "raw_bb", rb_cpShapeGetBB, 0);
|
|
473
|
+
|
|
474
|
+
rb_define_method(m_cpShape, "e", rb_cpShapeGetElasticity, 0);
|
|
475
|
+
rb_define_method(m_cpShape, "u", rb_cpShapeGetFriction, 0);
|
|
476
|
+
|
|
477
|
+
rb_define_method(m_cpShape, "e=", rb_cpShapeSetElasticity, 1);
|
|
478
|
+
rb_define_method(m_cpShape, "u=", rb_cpShapeSetFriction, 1);
|
|
479
|
+
|
|
480
|
+
rb_define_method(m_cpShape, "surface_v", rb_cpShapeGetSurfaceV, 0);
|
|
481
|
+
rb_define_method(m_cpShape, "surface_v=", rb_cpShapeSetSurfaceV, 1);
|
|
482
|
+
|
|
384
483
|
rb_define_method(m_cpShape, "point_query", rb_cpShapePointQuery, 1);
|
|
385
484
|
rb_define_method(m_cpShape, "segment_query", rb_cpShapeSegmentQuery, 2);
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
rb_define_singleton_method(m_cpShape, "reset_id_counter", rb_cpShapeResetIdCounter, 0);
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
c_cpCircleShape = rb_define_class_under(m_cpShape, "Circle", rb_cObject);
|
|
492
|
+
rb_include_module(c_cpCircleShape, m_cpShape);
|
|
493
|
+
rb_define_alloc_func(c_cpCircleShape, rb_cpCircleAlloc);
|
|
494
|
+
rb_define_method(c_cpCircleShape, "initialize", rb_cpCircleInitialize, -1);
|
|
495
|
+
|
|
496
|
+
c_cpSegmentShape = rb_define_class_under(m_cpShape, "Segment", rb_cObject);
|
|
497
|
+
rb_include_module(c_cpSegmentShape, m_cpShape);
|
|
498
|
+
rb_define_alloc_func(c_cpSegmentShape, rb_cpSegmentAlloc);
|
|
499
|
+
rb_define_method(c_cpSegmentShape, "initialize", rb_cpSegmentInitialize, 4);
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
c_cpPolyShape = rb_define_class_under(m_cpShape, "Poly", rb_cObject);
|
|
503
|
+
rb_include_module(c_cpPolyShape, m_cpShape);
|
|
504
|
+
rb_define_alloc_func(c_cpPolyShape, rb_cpPolyAlloc);
|
|
505
|
+
rb_define_singleton_method(c_cpPolyShape, "valid?", rb_cpPolyValidate, 1);
|
|
506
|
+
|
|
507
|
+
rb_define_method(c_cpPolyShape, "initialize", rb_cpPolyInitialize, -1);
|
|
508
|
+
|
|
409
509
|
rb_define_method(c_cpCircleShape, "offset", rb_cpCircleShapeGetOffset, 0);
|
|
410
510
|
rb_define_method(c_cpCircleShape, "radius", rb_cpCircleShapeGetRadius, 0);
|
|
411
|
-
rb_define_method(c_cpCircleShape, "r"
|
|
412
|
-
|
|
413
|
-
rb_define_method(c_cpSegmentShape, "a"
|
|
414
|
-
rb_define_method(c_cpSegmentShape, "b"
|
|
415
|
-
rb_define_method(c_cpSegmentShape, "radius"
|
|
416
|
-
rb_define_method(c_cpSegmentShape, "r"
|
|
417
|
-
rb_define_method(c_cpSegmentShape, "normal"
|
|
418
|
-
rb_define_method(c_cpSegmentShape, "n"
|
|
419
|
-
|
|
420
|
-
rb_define_method(c_cpPolyShape
|
|
421
|
-
rb_define_method(c_cpPolyShape
|
|
511
|
+
rb_define_method(c_cpCircleShape, "r", rb_cpCircleShapeGetRadius, 0);
|
|
512
|
+
|
|
513
|
+
rb_define_method(c_cpSegmentShape, "a", rb_cpSegmentShapeGetA, 0);
|
|
514
|
+
rb_define_method(c_cpSegmentShape, "b", rb_cpSegmentShapeGetB, 0);
|
|
515
|
+
rb_define_method(c_cpSegmentShape, "radius", rb_cpSegmentShapeGetRadius, 0);
|
|
516
|
+
rb_define_method(c_cpSegmentShape, "r", rb_cpSegmentShapeGetRadius, 0);
|
|
517
|
+
rb_define_method(c_cpSegmentShape, "normal", rb_cpSegmentShapeGetNormal, 0);
|
|
518
|
+
rb_define_method(c_cpSegmentShape, "n", rb_cpSegmentShapeGetNormal, 0);
|
|
519
|
+
|
|
520
|
+
rb_define_method(c_cpPolyShape, "num_verts", rb_cpPolyShapeGetNumVerts, 0);
|
|
521
|
+
rb_define_method(c_cpPolyShape, "vert", rb_cpPolyShapeGetVert, 1);
|
|
422
522
|
// also include an array-ish interface
|
|
423
|
-
rb_define_method(c_cpPolyShape
|
|
424
|
-
rb_define_method(c_cpPolyShape
|
|
425
|
-
rb_define_method(c_cpPolyShape
|
|
426
|
-
|
|
523
|
+
rb_define_method(c_cpPolyShape, "length", rb_cpPolyShapeGetNumVerts, 0);
|
|
524
|
+
rb_define_method(c_cpPolyShape, "size", rb_cpPolyShapeGetNumVerts, 0);
|
|
525
|
+
rb_define_method(c_cpPolyShape, "[]", rb_cpPolyShapeGetVert, 1);
|
|
526
|
+
// Not in API yet:
|
|
527
|
+
rb_define_method(c_cpPolyShape, "value_on_axis", rb_cpPolyShapeValueOnAxis, 2);
|
|
528
|
+
rb_define_method(c_cpPolyShape, "contains?", rb_cpPolyShapeContainsVert, 1);
|
|
529
|
+
rb_define_method(c_cpPolyShape, "contains_partial?", rb_cpPolyShapeContainsVertPartial, 2);
|
|
530
|
+
|
|
427
531
|
/* Use a struct for this small class. More efficient. */
|
|
428
532
|
c_cpSegmentQueryInfo = rb_struct_define("SegmentQueryInfo",
|
|
429
|
-
|
|
430
|
-
rb_define_const(m_Chipmunk, "SegmentQueryInfo", c_cpSegmentQueryInfo);
|
|
431
|
-
|
|
533
|
+
"shape", "t", "n", NULL);
|
|
534
|
+
rb_define_const(m_Chipmunk, "SegmentQueryInfo", c_cpSegmentQueryInfo);
|
|
535
|
+
/*"unsafe" API. */
|
|
536
|
+
rb_define_method(c_cpCircleShape, "set_radius!", rb_cpCircleShapeSetRadius, 1);
|
|
537
|
+
rb_define_method(c_cpCircleShape, "set_offset!", rb_cpCircleShapeSetOffset, 1);
|
|
538
|
+
rb_define_method(c_cpSegmentShape, "set_endpoints!", rb_cpSegmentShapeSetEndpoints, 2);
|
|
539
|
+
rb_define_method(c_cpSegmentShape, "set_radius!", rb_cpSegmentShapeSetRadius, 1);
|
|
540
|
+
|
|
541
|
+
rb_define_method(c_cpPolyShape, "set_verts!", rb_cpPolyShapeSetVerts, 2);
|
|
542
|
+
|
|
543
|
+
rb_define_method(m_cpShape, "collide!", rb_cpCollideShapes, 1);
|
|
544
|
+
|
|
432
545
|
}
|
|
433
|
-
//
|
|
546
|
+
//
|