rcad 0.1.0 → 0.1.1

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.
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