rcad 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/ext/_rcad/_rcad.cpp +316 -124
  2. data/lib/rcad.rb +238 -24
  3. data/lib/rcad/version.rb +1 -1
  4. metadata +2 -2
@@ -1,3 +1,4 @@
1
+ #include <sstream>
1
2
  #include <gp_Pnt2d.hxx>
2
3
  #include <gp_Pnt.hxx>
3
4
  #include <gp_Vec.hxx>
@@ -5,9 +6,11 @@
5
6
  #include <TColgp_Array2OfPnt.hxx>
6
7
  #include <Poly_Triangulation.hxx>
7
8
  #include <Geom_BezierSurface.hxx>
9
+ #include <Geom_Circle.hxx>
8
10
  #include <GCE2d_MakeSegment.hxx>
9
11
  #include <TopoDS.hxx>
10
12
  #include <BRepPrimAPI_MakeBox.hxx>
13
+ #include <BRepPrimAPI_MakeCone.hxx>
11
14
  #include <BRepPrimAPI_MakeCylinder.hxx>
12
15
  #include <BRepPrimAPI_MakeSphere.hxx>
13
16
  #include <BRepPrimAPI_MakeTorus.hxx>
@@ -29,6 +32,7 @@
29
32
  #include <BRepClass3d_SolidClassifier.hxx>
30
33
  #include <BRepTopAdaptor_FClass2d.hxx>
31
34
  #include <BRepMesh_IncrementalMesh.hxx>
35
+ #include <BRepBndLib.hxx>
32
36
  #include <StlAPI_Reader.hxx>
33
37
  #include <StlAPI_Writer.hxx>
34
38
  #include <Standard_Failure.hxx>
@@ -86,17 +90,17 @@ gp_Pnt2d from_ruby<gp_Pnt2d>(Object obj)
86
90
  }
87
91
 
88
92
  template<>
89
- gp_Pnt from_ruby<gp_Pnt>(Object obj)
93
+ gp_XYZ from_ruby<gp_XYZ>(Object obj)
90
94
  {
91
95
  Array ary(obj);
92
96
 
93
97
  if (ary.size() == 2) {
94
- return gp_Pnt(
98
+ return gp_XYZ(
95
99
  from_ruby<Standard_Real>(ary[0]),
96
100
  from_ruby<Standard_Real>(ary[1]),
97
101
  0);
98
102
  } else if (ary.size() == 3) {
99
- return gp_Pnt(
103
+ return gp_XYZ(
100
104
  from_ruby<Standard_Real>(ary[0]),
101
105
  from_ruby<Standard_Real>(ary[1]),
102
106
  from_ruby<Standard_Real>(ary[2]));
@@ -106,6 +110,12 @@ gp_Pnt from_ruby<gp_Pnt>(Object obj)
106
110
  }
107
111
  }
108
112
 
113
+ template<>
114
+ gp_Pnt from_ruby<gp_Pnt>(Object obj)
115
+ {
116
+ return gp_Pnt(from_ruby<gp_XYZ>(obj));
117
+ }
118
+
109
119
  template<>
110
120
  gp_Vec from_ruby<gp_Vec>(Object obj)
111
121
  {
@@ -128,85 +138,177 @@ gp_Dir from_ruby<gp_Dir>(Object obj)
128
138
  return gp_Dir(from_ruby<gp_Vec>(obj));
129
139
  }
130
140
 
131
-
132
- static Data_Object<TopoDS_Shape> render_shape(Object shape)
141
+ template<>
142
+ Object to_ruby<gp_Mat>(const gp_Mat &mat)
133
143
  {
134
- if (shape.is_a(rb_cRenderedShape)) {
135
- return shape;
136
- }
144
+ Array mat_ary;
137
145
 
138
- if (!shape.is_a(rb_cShape)) {
139
- String shape_str = shape.to_s();
140
- throw Exception(rb_eArgError,
141
- "attempt to render %s which is not a Shape",
142
- shape_str.c_str());
143
- }
146
+ for (int i = 1; i <= 3; ++i) {
147
+ Array row_ary;
148
+ for (int j = 1; j <= 3; ++j) {
149
+ row_ary.push(mat(i, j));
150
+ }
144
151
 
145
- while (shape.is_a(rb_cShape)) {
146
- shape = shape.call("render");
152
+ mat_ary.push(row_ary);
147
153
  }
148
154
 
149
- if (!shape.is_a(rb_cRenderedShape)) {
150
- String shape_str = shape.to_s();
151
- throw Exception(rb_eArgError,
152
- "render returned %s instead of a rendered shape",
153
- shape_str.c_str());
155
+ return mat_ary;
156
+ }
157
+
158
+ template<>
159
+ Object to_ruby<gp_XYZ>(const gp_XYZ &xyz)
160
+ {
161
+ Array ary;
162
+ ary.push(xyz.X());
163
+ ary.push(xyz.Y());
164
+ ary.push(xyz.Z());
165
+ return ary;
166
+ }
167
+
168
+
169
+ static Standard_Real get_tolerance()
170
+ {
171
+ return from_ruby<Standard_Real>(
172
+ Object(rb_gv_get("$tol")));
173
+ }
174
+
175
+
176
+ static String transform_to_s(gp_GTrsf self)
177
+ {
178
+ gp_Mat vec = self.VectorialPart();
179
+ gp_XYZ trn = self.TranslationPart();
180
+
181
+ std::stringstream s;
182
+ s << "#<Transform:";
183
+
184
+ for (int i = 1; i <= 3; ++i) {
185
+ for (int j = 1; j <= 3; ++j) {
186
+ s << vec(i, j)
187
+ << ((j < 3) ? ", " : "; ");
188
+ }
154
189
  }
155
190
 
156
- return shape;
191
+ s << "x=" << trn.X()
192
+ << ", y=" << trn.Y()
193
+ << ", z=" << trn.Z();
194
+
195
+ s << ">";
196
+ return s.str();
157
197
  }
158
198
 
159
- static Object wrap_rendered_shape(const TopoDS_Shape &shape)
199
+ static gp_Mat transform_mat_part(gp_GTrsf self)
160
200
  {
161
- Object shape_obj = rb_cShape.call("new");
162
- shape_obj.iv_set("@shape", to_ruby(shape));
163
- return shape_obj;
201
+ return self.VectorialPart();
164
202
  }
165
203
 
166
- static Object shape_transform(Object self, const gp_Trsf &transform)
204
+ static void transform_mat_part_set(gp_GTrsf &self, gp_Mat mat)
167
205
  {
168
- Data_Object<TopoDS_Shape> rendered = render_shape(self);
169
- return wrap_rendered_shape(
170
- BRepBuilderAPI_Transform(*rendered, transform, Standard_True).Shape());
206
+ self.SetVectorialPart(mat);
171
207
  }
172
208
 
173
- Object shape_move(Object self, Standard_Real x, Standard_Real y,
174
- Standard_Real z)
209
+ static gp_XYZ transform_ofs_part(gp_GTrsf self)
175
210
  {
176
- gp_Trsf transform;
177
- transform.SetTranslation(gp_Vec(x, y, z));
178
- return shape_transform(self, transform);
211
+ return self.TranslationPart();
179
212
  }
180
213
 
181
- Object shape_rotate(Object self, Standard_Real angle, gp_Dir axis)
214
+ static void transform_ofs_part_set(gp_GTrsf &self, gp_XYZ ofs)
182
215
  {
183
- gp_Trsf transform;
184
- transform.SetRotation(gp_Ax1(gp_Pnt(), axis), angle);
185
- return shape_transform(self, transform);
216
+ self.SetTranslationPart(ofs);
186
217
  }
187
218
 
188
- Object shape_scale(Object self, Standard_Real x, Standard_Real y,
219
+ static gp_GTrsf transform_multiply(gp_GTrsf self, gp_GTrsf right)
220
+ {
221
+ gp_GTrsf gtrsf = self;
222
+ gtrsf.Multiply(right);
223
+ return gtrsf;
224
+ }
225
+
226
+ static gp_GTrsf transform_move(gp_GTrsf self, Standard_Real x, Standard_Real y,
189
227
  Standard_Real z)
190
228
  {
191
- gp_GTrsf transform;
192
- transform.SetVectorialPart(
229
+ gp_GTrsf gtrsf;
230
+ gtrsf.SetTranslationPart(gp_XYZ(x, y, z));
231
+ gtrsf.Multiply(self); // gtrsf *= self
232
+ return gtrsf;
233
+ }
234
+
235
+ static gp_GTrsf transform_rotate(gp_GTrsf self, Standard_Real angle,
236
+ gp_Dir axis)
237
+ {
238
+ gp_Trsf rot;
239
+ rot.SetRotation(gp_Ax1(gp_Pnt(), axis), angle);
240
+
241
+ gp_GTrsf gtrsf(rot);
242
+ gtrsf.Multiply(self); // gtrsf *= self
243
+ return gtrsf;
244
+ }
245
+
246
+ static gp_GTrsf transform_scale(gp_GTrsf self,
247
+ Standard_Real x, Standard_Real y, Standard_Real z)
248
+ {
249
+ gp_GTrsf gtrsf;
250
+ gtrsf.SetVectorialPart(
193
251
  gp_Mat(x, 0, 0,
194
252
  0, y, 0,
195
253
  0, 0, z));
196
254
 
197
- Data_Object<TopoDS_Shape> rendered = render_shape(self);
198
- return wrap_rendered_shape(
199
- BRepBuilderAPI_GTransform(*rendered, transform, Standard_True).Shape());
255
+ gtrsf.Multiply(self); // gtrsf *= self
256
+ return gtrsf;
200
257
  }
201
258
 
202
- Object shape_mirror(Object self, Standard_Real x, Standard_Real y,
203
- Standard_Real z)
259
+ static gp_GTrsf transform_mirror(gp_GTrsf self,
260
+ Standard_Real x, Standard_Real y, Standard_Real z)
204
261
  {
205
262
  gp_Ax2 mirror_plane(gp::Origin(), gp_Dir(x, y, z));
206
263
 
207
- gp_Trsf transform;
208
- transform.SetMirror(mirror_plane);
209
- return shape_transform(self, transform);
264
+ gp_Trsf mirror;
265
+ mirror.SetMirror(mirror_plane);
266
+
267
+ gp_GTrsf gtrsf(mirror);
268
+ gtrsf.Multiply(self); // gtrsf *= self
269
+ return gtrsf;
270
+ }
271
+
272
+ static gp_GTrsf transform_inverse(gp_GTrsf self)
273
+ {
274
+ gp_GTrsf gtrsf = self;
275
+ gtrsf.Invert();
276
+ return gtrsf;
277
+ }
278
+
279
+
280
+ static Data_Object<TopoDS_Shape> render_shape(Object shape)
281
+ {
282
+ if (shape.is_a(rb_cRenderedShape)) {
283
+ return shape;
284
+ }
285
+
286
+ if (!shape.is_a(rb_cShape)) {
287
+ String shape_str = shape.to_s();
288
+ throw Exception(rb_eArgError,
289
+ "attempt to render %s which is not a Shape",
290
+ shape_str.c_str());
291
+ }
292
+
293
+ while (shape.is_a(rb_cShape)) {
294
+ shape = shape.call("render");
295
+ }
296
+
297
+ if (!shape.is_a(rb_cRenderedShape)) {
298
+ String shape_str = shape.to_s();
299
+ throw Exception(rb_eArgError,
300
+ "render returned %s instead of a rendered shape",
301
+ shape_str.c_str());
302
+ }
303
+
304
+ return shape;
305
+ }
306
+
307
+ // TODO: better to just put things in a Data_Object to begin with, than to
308
+ // allocate them twice
309
+ static Object wrap_rendered_shape(const TopoDS_Shape &shape)
310
+ {
311
+ return Data_Object<TopoDS_Shape>(new TopoDS_Shape(shape));
210
312
  }
211
313
 
212
314
  void shape_write_stl(Object self, String path)
@@ -216,10 +318,34 @@ void shape_write_stl(Object self, String path)
216
318
  StlAPI_Writer writer;
217
319
  writer.ASCIIMode() = false;
218
320
  writer.RelativeMode() = false;
219
- writer.SetDeflection(0.05); // TODO: deflection param
321
+ writer.SetDeflection(get_tolerance());
220
322
  writer.Write(*shape, path.c_str());
221
323
  }
222
324
 
325
+ Object shape__bbox(Object self)
326
+ {
327
+ Data_Object<TopoDS_Shape> shape = render_shape(self);
328
+
329
+ Standard_Real minXYZ[3];
330
+ Standard_Real maxXYZ[3];
331
+ Bnd_Box bbox;
332
+ BRepBndLib::Add(*shape, bbox);
333
+ bbox.Get(
334
+ minXYZ[0], minXYZ[1], minXYZ[2],
335
+ maxXYZ[0], maxXYZ[1], maxXYZ[2]);
336
+
337
+ const Standard_Real gap = bbox.GetGap();
338
+ for (int i = 0; i < 3; ++i) {
339
+ minXYZ[i] += gap;
340
+ maxXYZ[i] -= gap;
341
+ }
342
+
343
+ Array res;
344
+ res.push(Array(minXYZ));
345
+ res.push(Array(maxXYZ));
346
+ return res;
347
+ }
348
+
223
349
 
224
350
  Object shape_from_stl(String path)
225
351
  {
@@ -230,6 +356,17 @@ Object shape_from_stl(String path)
230
356
  }
231
357
 
232
358
 
359
+ Object transformed_shape_render(Object self)
360
+ {
361
+ gp_GTrsf transform = from_ruby<gp_GTrsf>(self.iv_get("@trsf"));
362
+
363
+ Object shape = self.iv_get("@shape");
364
+ Data_Object<TopoDS_Shape> rendered = render_shape(shape);
365
+
366
+ return wrap_rendered_shape(
367
+ BRepBuilderAPI_GTransform(*rendered, transform, Standard_True).Shape());
368
+ }
369
+
233
370
  static TopoDS_Wire make_wire_from_path(Array points, Array path)
234
371
  {
235
372
  BRepBuilderAPI_MakeWire wire_maker;
@@ -292,6 +429,16 @@ Object box_render(Object self)
292
429
  }
293
430
 
294
431
 
432
+ Object cone_render(Object self)
433
+ {
434
+ Standard_Real height = from_ruby<Standard_Real>(self.iv_get("@height"));
435
+ Standard_Real dia1 = from_ruby<Standard_Real>(self.iv_get("@bottom_dia"));
436
+ Standard_Real dia2 = from_ruby<Standard_Real>(self.iv_get("@top_dia"));
437
+ return to_ruby(
438
+ BRepPrimAPI_MakeCone(dia1 / 2.0, dia2 / 2.0, height).Shape());
439
+ }
440
+
441
+
295
442
  Object cylinder_render(Object self)
296
443
  {
297
444
  Standard_Real height = from_ruby<Standard_Real>(self.iv_get("@height"));
@@ -420,50 +567,21 @@ static bool is_inner_wire_of_face(TopoDS_Wire wire, TopoDS_Face face)
420
567
  return (fclass2D.PerformInfinitePoint() != TopAbs_OUT);
421
568
  }
422
569
 
423
- static TopoDS_Shape twist_extrude_wire(TopoDS_Wire wire, Standard_Real height,
424
- Standard_Real twist)
570
+ static TopoDS_Shape extrude_wire(TopoDS_Wire profile, TopoDS_Wire spine,
571
+ TopoDS_Face spine_support)
425
572
  {
426
- // split height into segments. each segment will twist no more than
427
- // 90 degrees.
428
- const int num_twist_segments = (int)(fabs(twist) / M_PI_2 + 1);
429
- // note that height and twist are doubles so division is not integer
430
- // division
431
- const Standard_Real seg_height = height / num_twist_segments;
432
- const Standard_Real seg_twist = twist / num_twist_segments;
433
-
434
- Handle_Geom_BezierSurface surf_hnd(
435
- new Geom_BezierSurface(
436
- TColgp_Array2OfPnt(
437
- 1, num_twist_segments + 1,
438
- 1, 2)));
439
-
440
- for (int i = 1; i <= num_twist_segments + 1; ++i) {
441
- const Standard_Real z = seg_height * (i - 1);
442
- const Standard_Real angle = seg_twist * (i - 1);
443
- surf_hnd->SetPole(i, 1, gp_Pnt(0, 0, z));
444
- surf_hnd->SetPole(i, 2, gp_Pnt(cos(angle), sin(angle), z));
445
- }
446
-
447
- TopoDS_Face spine_support =
448
- // TODO: tolerance
449
- BRepBuilderAPI_MakeFace(surf_hnd, 0, 1, 0, 1,
450
- Precision::Confusion());
451
-
452
- Handle_Geom2d_Curve uv_curve_hnd =
453
- GCE2d_MakeSegment(gp_Pnt2d(0, 0), gp_Pnt2d(1, 0));
454
- TopoDS_Edge spine = BRepBuilderAPI_MakeEdge(uv_curve_hnd, surf_hnd);
455
- TopoDS_Wire spine_wire = BRepBuilderAPI_MakeWire(spine);
456
-
457
-
458
- BRepOffsetAPI_MakePipeShell pipe_maker(spine_wire);
573
+ BRepOffsetAPI_MakePipeShell pipe_maker(spine);
459
574
 
460
- if (!pipe_maker.SetMode(spine_support)) {
461
- throw Exception(rb_cOCEError,
462
- "failed setting twisted surface-normal for PipeShell");
575
+ if (!spine_support.IsNull()) {
576
+ if (!pipe_maker.SetMode(spine_support)) {
577
+ throw Exception(rb_cOCEError,
578
+ "failed setting twisted surface-normal for PipeShell");
579
+ }
463
580
  }
464
581
 
465
- pipe_maker.Add(wire);
466
- pipe_maker.SetTolerance(0.05, 0.05); // TODO: tolerance
582
+ pipe_maker.Add(profile);
583
+ const Standard_Real tolerance = get_tolerance();
584
+ pipe_maker.SetTolerance(tolerance, tolerance);
467
585
  pipe_maker.Build();
468
586
 
469
587
  if (!pipe_maker.MakeSolid()) {
@@ -473,8 +591,8 @@ static TopoDS_Shape twist_extrude_wire(TopoDS_Wire wire, Standard_Real height,
473
591
  return pipe_maker.Shape();
474
592
  }
475
593
 
476
- static TopoDS_Shape twist_extrude_face(TopoDS_Face face, Standard_Real height,
477
- Standard_Real twist)
594
+ static TopoDS_Shape extrude_face(TopoDS_Face profile, TopoDS_Wire spine,
595
+ TopoDS_Face spine_support)
478
596
  {
479
597
  // extrude outer and inner wires separately, then subtract the inner
480
598
  // shapes from the outer shape. there should be only one outer shape,
@@ -488,10 +606,10 @@ static TopoDS_Shape twist_extrude_face(TopoDS_Face face, Standard_Real height,
488
606
 
489
607
  TopExp_Explorer texp;
490
608
 
491
- TopoDS_Face orface = TopoDS::Face(face.Oriented(TopAbs_FORWARD));
609
+ TopoDS_Face orface = TopoDS::Face(profile.Oriented(TopAbs_FORWARD));
492
610
  for (texp.Init(orface, TopAbs_WIRE); texp.More(); texp.Next()) {
493
611
  TopoDS_Wire wire = TopoDS::Wire(texp.Current());
494
- TopoDS_Shape ext_wire = twist_extrude_wire(wire, height, twist);
612
+ TopoDS_Shape ext_wire = extrude_wire(wire, spine, spine_support);
495
613
 
496
614
  if (is_inner_wire_of_face(wire, orface)) {
497
615
  builder.Add(inner, ext_wire);
@@ -503,6 +621,60 @@ static TopoDS_Shape twist_extrude_face(TopoDS_Face face, Standard_Real height,
503
621
  return BRepAlgoAPI_Cut(outer, inner).Shape();
504
622
  }
505
623
 
624
+ static TopoDS_Shape extrude_shape(TopoDS_Shape profile, TopoDS_Wire spine,
625
+ TopoDS_Face spine_support)
626
+ {
627
+ BRep_Builder builder;
628
+ TopoDS_Compound compound;
629
+ builder.MakeCompound(compound);
630
+
631
+ TopExp_Explorer texp;
632
+ for (texp.Init(profile, TopAbs_FACE); texp.More(); texp.Next()) {
633
+ builder.Add(compound,
634
+ extrude_face(
635
+ TopoDS::Face(texp.Current()), spine, spine_support));
636
+ }
637
+
638
+ return compound;
639
+ }
640
+
641
+ static TopoDS_Shape twist_extrude(TopoDS_Shape shape, Standard_Real height,
642
+ Standard_Real twist)
643
+ {
644
+ // split height into segments. each segment will twist no more than
645
+ // 90 degrees.
646
+ const int num_twist_segments = (int)(fabs(twist) / M_PI_2 + 1);
647
+ // note that height and twist are doubles so division is not integer
648
+ // division
649
+ const Standard_Real seg_height = height / num_twist_segments;
650
+ const Standard_Real seg_twist = twist / num_twist_segments;
651
+
652
+ Handle_Geom_BezierSurface surf_hnd(
653
+ new Geom_BezierSurface(
654
+ TColgp_Array2OfPnt(
655
+ 1, num_twist_segments + 1,
656
+ 1, 2)));
657
+
658
+ for (int i = 1; i <= num_twist_segments + 1; ++i) {
659
+ const Standard_Real z = seg_height * (i - 1);
660
+ const Standard_Real angle = seg_twist * (i - 1);
661
+ surf_hnd->SetPole(i, 1, gp_Pnt(0, 0, z));
662
+ surf_hnd->SetPole(i, 2, gp_Pnt(cos(angle), sin(angle), z));
663
+ }
664
+
665
+ TopoDS_Face spine_support =
666
+ // TODO: tolerance
667
+ BRepBuilderAPI_MakeFace(surf_hnd, 0, 1, 0, 1,
668
+ Precision::Confusion());
669
+
670
+ Handle_Geom2d_Curve uv_curve_hnd =
671
+ GCE2d_MakeSegment(gp_Pnt2d(0, 0), gp_Pnt2d(1, 0));
672
+ TopoDS_Edge spine = BRepBuilderAPI_MakeEdge(uv_curve_hnd, surf_hnd);
673
+ TopoDS_Wire spine_wire = BRepBuilderAPI_MakeWire(spine);
674
+
675
+ return extrude_shape(shape, spine_wire, spine_support);
676
+ }
677
+
506
678
  // initialize is defined in Ruby code
507
679
  Object linear_extrusion_render(Object self)
508
680
  {
@@ -517,38 +689,35 @@ Object linear_extrusion_render(Object self)
517
689
  BRepPrimAPI_MakePrism(*shape, gp_Vec(0, 0, height),
518
690
  Standard_True).Shape());
519
691
  } else {
520
- BRep_Builder builder;
521
- TopoDS_Compound compound;
522
- builder.MakeCompound(compound);
523
-
524
- TopExp_Explorer texp;
525
- for (texp.Init(*shape, TopAbs_FACE); texp.More(); texp.Next()) {
526
- builder.Add(compound,
527
- twist_extrude_face(
528
- TopoDS::Face(texp.Current()), height, twist));
529
- }
530
-
531
- return wrap_rendered_shape(compound);
692
+ return wrap_rendered_shape(twist_extrude(*shape, height, twist));
532
693
  }
533
694
  }
534
695
 
535
696
 
536
- // initialize is defined in Ruby code
537
697
  Object revolution_render(Object self)
538
698
  {
539
699
  Object profile = self.iv_get("@profile");
540
700
  Data_Object<TopoDS_Shape> shape = render_shape(profile);
541
701
 
542
702
  Object angle = self.iv_get("@angle");
703
+
704
+ gp_Circ circ(gp_Ax2(gp::Origin(), -gp::DY(), gp::DX()), 1.0);
705
+ TopoDS_Edge edge;
706
+
543
707
  if (angle.is_nil()) {
544
- return to_ruby(
545
- BRepPrimAPI_MakeRevol(*shape, gp::OY(), Standard_True).Shape());
708
+ edge = BRepBuilderAPI_MakeEdge(circ);
546
709
  } else {
547
710
  Standard_Real angle_num = from_ruby<Standard_Real>(angle);
548
- return to_ruby(
549
- BRepPrimAPI_MakeRevol(*shape, gp::OY(), angle_num,
550
- Standard_True).Shape());
711
+ angle_num = std::max(angle_num, 0.0);
712
+ angle_num = std::min(angle_num, M_PI * 2);
713
+
714
+ Handle_Geom_Curve curve_hnd(new Geom_Circle(circ));
715
+ edge = BRepBuilderAPI_MakeEdge(curve_hnd, 0, angle_num);
551
716
  }
717
+
718
+ TopoDS_Wire spine = BRepBuilderAPI_MakeWire(edge);
719
+ return wrap_rendered_shape(
720
+ extrude_shape(*shape, spine, TopoDS_Face()));
552
721
  }
553
722
 
554
723
 
@@ -581,11 +750,13 @@ static std::vector<gp_Pnt> get_points_from_shapes(Array shapes)
581
750
  {
582
751
  std::vector<gp_Pnt> points;
583
752
 
753
+ const Standard_Real tolerance = get_tolerance();
754
+
584
755
  for (size_t i = 0; i < shapes.size(); ++i) {
585
756
  Data_Object<TopoDS_Shape> shape_obj = render_shape(shapes[i]);
586
757
  const TopoDS_Shape &shape = *shape_obj;
587
758
 
588
- BRepMesh_IncrementalMesh(shape, 0.05); // TODO: tolerance
759
+ BRepMesh_IncrementalMesh(shape, tolerance);
589
760
 
590
761
  get_points_from_shape(shape, points);
591
762
  }
@@ -594,13 +765,13 @@ static std::vector<gp_Pnt> get_points_from_shapes(Array shapes)
594
765
  }
595
766
 
596
767
 
597
- // Sort function object to sort polygon vertices
768
+ // Sort function object to sort the vertices of a convex polygon
598
769
  // Algorithm from here:
599
770
  // http://stackoverflow.com/a/15104911/2758814
600
- class PolygonVertexSortComparator
771
+ class ConvexPolygonVertexSortComparator
601
772
  {
602
773
  public:
603
- PolygonVertexSortComparator(std::vector<gp_Pnt> vertices)
774
+ ConvexPolygonVertexSortComparator(std::vector<gp_Pnt> vertices)
604
775
  : vertices(vertices)
605
776
  {
606
777
  if (vertices.size() == 0) {
@@ -673,7 +844,7 @@ static TopoDS_Solid make_solid_from_qhull()
673
844
  }
674
845
 
675
846
  sort(vertices.begin(), vertices.end(),
676
- PolygonVertexSortComparator(vertices));
847
+ ConvexPolygonVertexSortComparator(vertices));
677
848
 
678
849
  BRepBuilderAPI_MakePolygon poly_maker;
679
850
  for (size_t i = 0; i < vertices.size(); ++i) {
@@ -734,21 +905,38 @@ static Object _hull(Array shapes)
734
905
  extern "C"
735
906
  void Init__rcad()
736
907
  {
737
- Data_Type<TopoDS_Shape> rb_cRenderedShape =
738
- define_class<TopoDS_Shape>("RenderedShape");
908
+ rb_cRenderedShape = define_class<TopoDS_Shape>("RenderedShape");
739
909
 
740
910
  Data_Type<Standard_Failure> rb_cOCEError =
741
911
  define_class("OCEError", rb_eRuntimeError);
742
912
 
913
+ Class rb_cTransform = define_class<gp_GTrsf>("Transform")
914
+ .add_handler<Standard_Failure>(translate_oce_exception)
915
+ .define_method("to_s", &transform_to_s)
916
+ .define_method("mat_part", &transform_mat_part)
917
+ .define_method("mat_part=", &transform_mat_part_set)
918
+ .define_method("ofs_part", &transform_ofs_part)
919
+ .define_method("ofs_part=", &transform_ofs_part_set)
920
+ .define_method("*", &transform_multiply)
921
+ .define_method("move", &transform_move)
922
+ .define_method("rotate", &transform_rotate)
923
+ .define_method("scale", &transform_scale)
924
+ .define_method("mirror", &transform_mirror)
925
+ .define_method("inverse", &transform_inverse);
926
+
927
+ rb_const_set(rb_cObject, Identifier("I"), to_ruby<gp_GTrsf>(gp_GTrsf()));
928
+
929
+
743
930
  rb_cShape = define_class("Shape")
744
931
  .add_handler<Standard_Failure>(translate_oce_exception)
745
- .define_method("move", &shape_move)
746
- .define_method("rotate", &shape_rotate)
747
- .define_method("scale", &shape_scale)
748
- .define_method("mirror", &shape_mirror)
749
932
  .define_method("write_stl", &shape_write_stl)
933
+ .define_method("_bbox", &shape__bbox)
750
934
  .define_singleton_method("from_stl", &shape_from_stl);
751
935
 
936
+ Class rb_cTransformedShape = define_class("TransformedShape", rb_cShape)
937
+ .add_handler<Standard_Failure>(translate_oce_exception)
938
+ .define_method("render", &transformed_shape_render);
939
+
752
940
  Class rb_cPolygon = define_class("Polygon", rb_cShape)
753
941
  .add_handler<Standard_Failure>(translate_oce_exception)
754
942
  .define_method("render", &polygon_render);
@@ -762,6 +950,10 @@ void Init__rcad()
762
950
  .add_handler<Standard_Failure>(translate_oce_exception)
763
951
  .define_method("render", &box_render);
764
952
 
953
+ Class rb_cCone = define_class("Cone", rb_cShape)
954
+ .add_handler<Standard_Failure>(translate_oce_exception)
955
+ .define_method("render", &cone_render);
956
+
765
957
  Class rb_cCylinder = define_class("Cylinder", rb_cShape)
766
958
  .add_handler<Standard_Failure>(translate_oce_exception)
767
959
  .define_method("render", &cylinder_render);
@@ -29,12 +29,64 @@ class Numeric
29
29
  end
30
30
 
31
31
 
32
+ # global tolerance value used by C++ extension when rendering shapes
33
+ $tol = 50.um
34
+
35
+
32
36
  def to_polar(r, a)
33
37
  return [r * Math::cos(a), r * Math::sin(a)]
34
38
  end
35
39
 
36
40
 
37
- TOLERANCE = 50.um
41
+ class Transform
42
+ def move_x(delta)
43
+ move(delta, 0, 0)
44
+ end
45
+
46
+ def move_y(delta)
47
+ move(0, delta, 0)
48
+ end
49
+
50
+ def move_z(delta)
51
+ move(0, 0, delta)
52
+ end
53
+
54
+ def rot_x(angle)
55
+ rotate(angle, [1, 0, 0])
56
+ end
57
+
58
+ def rot_y(angle)
59
+ rotate(angle, [0, 1, 0])
60
+ end
61
+
62
+ def rot_z(angle)
63
+ rotate(angle, [0, 0, 1])
64
+ end
65
+
66
+ def scale_x(factor)
67
+ scale(factor, 1, 1)
68
+ end
69
+
70
+ def scale_y(factor)
71
+ scale(1, factor, 1)
72
+ end
73
+
74
+ def scale_z(factor)
75
+ scale(1, 1, factor)
76
+ end
77
+
78
+ def mirror_x
79
+ mirror(1, 0, 0)
80
+ end
81
+
82
+ def mirror_y
83
+ mirror(0, 1, 0)
84
+ end
85
+
86
+ def mirror_z
87
+ mirror(0, 0, 1)
88
+ end
89
+ end
38
90
 
39
91
 
40
92
  class Shape
@@ -63,9 +115,30 @@ class Shape
63
115
  $shape = ($shape == nil) ? self : $shape.send($shape_mode, self)
64
116
  end
65
117
 
118
+ p self
66
119
  self
67
120
  end
68
121
 
122
+ def transform(trsf)
123
+ TransformedShape.new(self, trsf)
124
+ end
125
+
126
+ def move(x, y, z)
127
+ TransformedShape.new(self, I.move(x, y, z))
128
+ end
129
+
130
+ def rotate(angle, axis)
131
+ TransformedShape.new(self, I.rotate(angle, axis))
132
+ end
133
+
134
+ def scale(x, y, z)
135
+ TransformedShape.new(self, I.scale(x, y, z))
136
+ end
137
+
138
+ def mirror(x, y, z)
139
+ TransformedShape.new(self, I.mirror(x, y, z))
140
+ end
141
+
69
142
  def move_x(delta)
70
143
  move(delta, 0, 0)
71
144
  end
@@ -118,48 +191,120 @@ class Shape
118
191
  LinearExtrusion.new(self, height, twist)
119
192
  end
120
193
 
121
- def revolve(angle=360.deg_to_rad)
194
+ def revolve(angle=nil)
122
195
  Revolution.new(self, angle)
123
196
  end
124
197
 
125
198
  def bbox
126
- # TODO
199
+ if @bbox == nil
200
+ @bbox = _bbox
201
+ end
202
+
203
+ @bbox
204
+ end
205
+
206
+ def minx
207
+ bbox[0][0]
127
208
  end
128
209
 
129
- def min_x
130
- bbox[0].x
210
+ def miny
211
+ bbox[0][1]
131
212
  end
132
213
 
133
- def min_y
134
- bbox[0].y
214
+ def minz
215
+ bbox[0][2]
135
216
  end
136
217
 
137
- def min_z
138
- bbox[0].z
218
+ def maxx
219
+ bbox[1][0]
139
220
  end
140
221
 
141
- def max_x
142
- bbox[1].x
222
+ def maxy
223
+ bbox[1][1]
143
224
  end
144
225
 
145
- def max_y
146
- bbox[1].y
226
+ def maxz
227
+ bbox[1][2]
147
228
  end
148
229
 
149
- def max_z
150
- bbox[1].z
230
+ def xsize
231
+ maxx - minx
151
232
  end
152
233
 
153
- def x_size
154
- max_x - min_x
234
+ def ysize
235
+ maxy - miny
155
236
  end
156
237
 
157
- def y_size
158
- max_y - min_y
238
+ def zsize
239
+ maxz - minz
159
240
  end
160
241
 
161
- def z_size
162
- max_z - min_z
242
+ def cx
243
+ (minx + maxx) / 2.0
244
+ end
245
+
246
+ def cy
247
+ (miny + maxy) / 2.0
248
+ end
249
+
250
+ def cz
251
+ (minz + maxz) / 2.0
252
+ end
253
+
254
+ def left
255
+ I.move_x(minx)
256
+ end
257
+
258
+ def right
259
+ I.move_x(maxx)
260
+ end
261
+
262
+ def front
263
+ I.move_y(miny)
264
+ end
265
+
266
+ def back
267
+ I.move_y(maxy)
268
+ end
269
+
270
+ def top
271
+ I.move_z(maxz)
272
+ end
273
+
274
+ def bottom
275
+ I.move_z(minz)
276
+ end
277
+
278
+ def xcenter
279
+ I.move_x(cx)
280
+ end
281
+
282
+ def ycenter
283
+ I.move_y(cy)
284
+ end
285
+
286
+ def zcenter
287
+ I.move_z(cz)
288
+ end
289
+
290
+ def center
291
+ I.move(cx, cy, cz)
292
+ end
293
+
294
+ def align(*align_pts)
295
+ at = align_pts.pop
296
+
297
+ if align_pts.empty?
298
+ return self.transform(at)
299
+ end
300
+
301
+ combined = align_pts
302
+ .map { |apt| (apt.is_a? Transform) ? apt : self.send(apt) }
303
+ .reduce :*
304
+
305
+ combined = (yield self) * combined if block_given?
306
+
307
+ self.transform(at * combined.inverse)
163
308
  end
164
309
  end
165
310
 
@@ -221,7 +366,41 @@ at_exit do
221
366
  end
222
367
 
223
368
 
224
- class Polygon
369
+ class TransformedShape < Shape
370
+ attr_reader :shape, :trsf
371
+
372
+ def initialize(shape, trsf)
373
+ @shape = shape
374
+ @trsf = trsf
375
+ end
376
+
377
+ def to_s
378
+ sprintf("%s*%s", @trsf, @shape)
379
+ end
380
+
381
+ def transform(trsf)
382
+ TransformedShape.new(@shape, trsf * @trsf)
383
+ end
384
+
385
+ def move(x, y, z)
386
+ TransformedShape.new(@shape, @trsf.move(x, y, z))
387
+ end
388
+
389
+ def rotate(angle, axis)
390
+ TransformedShape.new(@shape, @trsf.rotate(angle, axis))
391
+ end
392
+
393
+ def scale(x, y, z)
394
+ TransformedShape.new(@shape, @trsf.scale(x, y, z))
395
+ end
396
+
397
+ def mirror(x, y, z)
398
+ TransformedShape.new(@shape, @trsf.mirror(x, y, z))
399
+ end
400
+ end
401
+
402
+
403
+ class Polygon < Shape
225
404
  attr_reader :points, :paths
226
405
 
227
406
  def initialize(points, paths=nil)
@@ -245,13 +424,26 @@ class RegularPolygon < Polygon
245
424
  end
246
425
 
247
426
 
248
- class Square < Polygon
427
+ class Rectangle < Polygon
428
+ # These override Shape.xsize etc.
429
+ attr_reader :xsize, :ysize
430
+
431
+ def initialize(xsize, ysize)
432
+ @xsize = xsize
433
+ @ysize = ysize
434
+
435
+ super([[0,0], [xsize,0], [xsize,ysize], [0,ysize]])
436
+ end
437
+ end
438
+
439
+
440
+ class Square < Rectangle
249
441
  attr_reader :size
250
442
 
251
443
  def initialize(size)
252
444
  @size = size
253
445
 
254
- super([[0,0], [size,0], [size,size], [0,size]])
446
+ super(size, size)
255
447
  end
256
448
  end
257
449
 
@@ -271,6 +463,7 @@ end
271
463
 
272
464
 
273
465
  class Box < Shape
466
+ # These override Shape.xsize etc.
274
467
  attr_accessor :xsize, :ysize, :zsize
275
468
 
276
469
  def initialize(xsize, ysize, zsize)
@@ -287,6 +480,25 @@ class Cube < Box
287
480
  end
288
481
 
289
482
 
483
+ class Cone < Shape
484
+ attr_accessor :height, :bottom_dia, :top_dia
485
+
486
+ def initialize(height, bottom_dia, top_dia=0)
487
+ @height = height
488
+ @bottom_dia = bottom_dia
489
+ @top_dia = top_dia
490
+ end
491
+
492
+ def bottom_radius
493
+ bottom_dia / 2.0
494
+ end
495
+
496
+ def top_radius
497
+ top_dia / 2.0
498
+ end
499
+ end
500
+
501
+
290
502
  class Cylinder < Shape
291
503
  attr_accessor :height, :dia
292
504
 
@@ -383,12 +595,14 @@ end
383
595
 
384
596
  make_maker :polygon, Polygon
385
597
  make_maker :reg_poly, RegularPolygon
598
+ make_maker :rectangle, Rectangle
386
599
  make_maker :square, Square
387
600
  make_maker :circle, Circle
388
601
  make_maker :text, Text
389
602
 
390
603
  make_maker :box, Box
391
604
  make_maker :cube, Cube
605
+ make_maker :cone, Cone
392
606
  make_maker :cylinder, Cylinder
393
607
  make_maker :sphere, Sphere
394
608
  make_maker :polyhedron, Polyhedron
@@ -1,3 +1,3 @@
1
1
  module Rcad
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rcad
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-11-03 00:00:00.000000000 Z
12
+ date: 2013-11-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler