femagtools 1.6.8__py3-none-any.whl → 1.7.0__py3-none-any.whl

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 (50) hide show
  1. femagtools/__init__.py +2 -2
  2. femagtools/bch.py +1 -1
  3. femagtools/dxfsl/area.py +334 -332
  4. femagtools/dxfsl/areabuilder.py +131 -10
  5. femagtools/dxfsl/conv.py +27 -9
  6. femagtools/dxfsl/converter.py +390 -125
  7. femagtools/dxfsl/corner.py +3 -0
  8. femagtools/dxfsl/femparser.py +1 -1
  9. femagtools/dxfsl/fslrenderer.py +290 -246
  10. femagtools/dxfsl/functions.py +4 -2
  11. femagtools/dxfsl/geom.py +1120 -886
  12. femagtools/dxfsl/journal.py +53 -22
  13. femagtools/dxfsl/machine.py +250 -74
  14. femagtools/dxfsl/plotrenderer.py +34 -3
  15. femagtools/dxfsl/shape.py +380 -103
  16. femagtools/dxfsl/symmetry.py +679 -110
  17. femagtools/femag.py +27 -2
  18. femagtools/forcedens.py +65 -40
  19. femagtools/fsl.py +71 -28
  20. femagtools/losscoeffs.py +46 -0
  21. femagtools/machine/effloss.py +8 -1
  22. femagtools/machine/im.py +3 -1
  23. femagtools/machine/pm.py +11 -7
  24. femagtools/machine/sizing.py +14 -11
  25. femagtools/machine/sm.py +114 -33
  26. femagtools/machine/utils.py +38 -34
  27. femagtools/model.py +12 -2
  28. femagtools/moo/population.py +1 -1
  29. femagtools/parstudy.py +17 -1
  30. femagtools/plot/__init__.py +1 -1
  31. femagtools/plot/char.py +24 -7
  32. femagtools/plot/forcedens.py +56 -29
  33. femagtools/plot/mcv.py +4 -1
  34. femagtools/plot/phasor.py +6 -1
  35. femagtools/poc.py +17 -10
  36. femagtools/templates/cogg_calc.mako +7 -1
  37. femagtools/templates/displ_stator_rotor.mako +33 -0
  38. femagtools/templates/fieldcalc.mako +10 -16
  39. femagtools/templates/pm_sym_f_cur.mako +1 -1
  40. femagtools/tks.py +3 -9
  41. {femagtools-1.6.8.dist-info → femagtools-1.7.0.dist-info}/LICENSE +1 -0
  42. {femagtools-1.6.8.dist-info → femagtools-1.7.0.dist-info}/METADATA +7 -4
  43. {femagtools-1.6.8.dist-info → femagtools-1.7.0.dist-info}/RECORD +50 -49
  44. tests/engines/__init__.py +0 -20
  45. tests/geom/__init__.py +0 -20
  46. tests/moo/__init__.py +0 -20
  47. tests/test_model.py +8 -1
  48. {femagtools-1.6.8.dist-info → femagtools-1.7.0.dist-info}/WHEEL +0 -0
  49. {femagtools-1.6.8.dist-info → femagtools-1.7.0.dist-info}/entry_points.txt +0 -0
  50. {femagtools-1.6.8.dist-info → femagtools-1.7.0.dist-info}/top_level.txt +0 -0
femagtools/dxfsl/shape.py CHANGED
@@ -16,8 +16,8 @@ from .functions import point, points_are_close, points_on_arc
16
16
  from .functions import alpha_line, alpha_angle, alpha_triangle
17
17
  from .functions import normalise_angle, min_angle, max_angle, get_angle_of_arc
18
18
  from .functions import lines_intersect_point, nodes_are_equal
19
- from .functions import is_angle_inside, intersect_point
20
- from .functions import middle_angle, middle_point_of_line
19
+ from .functions import is_angle_inside, intersect_point, point_greater_equal
20
+ from .functions import middle_angle, middle_point_of_line, elevation_angle
21
21
 
22
22
  logger = logging.getLogger('femagtools.geom')
23
23
 
@@ -116,12 +116,8 @@ class Shape(object):
116
116
  def dy(self):
117
117
  return (self.p2[1]-self.p1[1])
118
118
 
119
- def m(self, none_val=None):
120
- m = line_m(self.p1, self.p2)
121
- if m is None:
122
- return none_val
123
- else:
124
- return m
119
+ def m(self, none_val=None, dec=0):
120
+ return line_m(self.p1, self.p2, none_val=none_val, dec=dec)
125
121
 
126
122
  def n(self, m):
127
123
  return line_n(self.p1, m)
@@ -213,7 +209,10 @@ class Shape(object):
213
209
  if d1 == d2:
214
210
  logger.warning("distances of %s and %s are equal (%s / %s)",
215
211
  self.n1, self.n2, d1, d2)
216
- raise ValueError('both nodes are equal in element')
212
+ #raise ValueError('both nodes are equal in element')
213
+ logger.warning("Points of %s are %s and %s",
214
+ self.classname(), self.p1, self.p2)
215
+ return 0
217
216
 
218
217
  if d1 < d2:
219
218
  return 1
@@ -232,48 +231,86 @@ class Shape(object):
232
231
  return 0.0
233
232
 
234
233
  def minmax_angle_dist_from_center(self,
235
- min_alfa, max_alfa,
236
- center, circ):
237
- logger.debug("minmax_angle_dist_from_center(center={}, circle={}"
238
- .format(center, circ))
239
-
234
+ min_alfa,
235
+ max_alfa,
236
+ center,
237
+ circ):
240
238
  points = self.intersect_circle(circ, include_end=True)
241
- logger.debug("self={}".format(self))
242
- logger.debug("points={}".format(points))
243
239
  if not points:
244
- logger.debug(" - no points")
245
240
  return None
246
241
  my_min_angle = min_alfa
247
242
  my_max_angle = max_alfa
248
- logger.debug(" - min {}, max {}"
249
- .format(my_min_angle, my_max_angle))
250
243
  for p in points:
251
244
  alpha = alpha_line(center, p)
252
245
  my_min_angle = min_angle(my_min_angle, alpha)
253
246
  my_max_angle = max_angle(my_max_angle, alpha)
254
- logger.debug(" - min {}, max {}, alpha {}"
255
- .format(my_min_angle, my_max_angle, alpha))
256
247
 
257
- logger.debug("end of minmax_angle_dist_from_center")
258
248
  return (my_min_angle, my_max_angle)
259
249
 
260
- def concatenate(self, n1, n2, el):
250
+ def concatenate(self, n1, n2, el,
251
+ rtol=1e-05, atol=1e-05,
252
+ mdec=0,
253
+ overlapping=False):
261
254
  if isinstance(el, Line):
262
- return self.concatenate_line(n1, n2, el)
255
+ return self.concatenate_line(n1, n2, el,
256
+ rtol=rtol, atol=atol,
257
+ mdec=mdec,
258
+ overlapping=overlapping)
263
259
  if isinstance(el, Arc):
264
- return self.concatenate_arc(n1, n2, el)
260
+ return self.concatenate_arc(n1, n2, el,
261
+ rtol=rtol, atol=atol,
262
+ overlapping=overlapping)
263
+
264
+ if isinstance(el, Circle):
265
+ return self.concatenate_circle(n1, n2, el,
266
+ rtol=rtol, atol=atol,
267
+ overlapping=overlapping)
265
268
  return None
266
269
 
267
- def concatenate_line(self, n1, n2, el):
270
+ def concatenate_line(self, n1, n2, el,
271
+ rtol=1e-05, atol=1e-05,
272
+ mdec=0,
273
+ overlapping=False):
268
274
  return None
269
275
 
270
- def concatenate_arc(self, n1, n2, el):
276
+ def concatenate_arc(self, n1, n2, el,
277
+ rtol=1e-05, atol=1e-05,
278
+ overlapping=False):
271
279
  return None
272
280
 
281
+ def concatenate_circle(self, n1, n2, el,
282
+ rtol=1e-05, atol=1e-05,
283
+ overlapping=False):
284
+ return None
285
+
286
+ def points_sorted(self, rtol=1e-05, atol=1e-05):
287
+ if point_greater_equal(self.p1, self.p2, rtol=rtol, atol=atol):
288
+ return self.p2, self.p1
289
+ else:
290
+ return self.p1, self.p2
291
+
273
292
  def rotate(self, T, p):
274
293
  n = T.dot(np.array((p[0], p[1])))
275
294
  return (n[0], n[1])
276
295
 
296
+ def is_tiny(self, mindist):
297
+ return distance(self.p1, self.p2) < mindist
298
+
299
+ def adjust_points(self):
300
+ self.p1 = self.n1
301
+ self.p2 = self.n2
302
+
303
+ def replace_point(self, node, point):
304
+ if nodes_are_equal(node, self.n1):
305
+ self.p1 = point
306
+ elif nodes_are_equal(node, self.n2):
307
+ self.p2 = point
308
+ else:
309
+ logger.warning("Node %s not in element", node)
310
+
311
+ def is_near(self, n):
312
+ return False
313
+
277
314
  def print_nodes(self):
278
315
  return " n1={}/n2={}".format(self.n1, self.n2)
279
316
 
@@ -304,6 +341,8 @@ class Circle(Shape):
304
341
  center = e.center
305
342
  self.center = lf*center[0] + xoff, lf*center[1] + yoff
306
343
  self.radius = lf*e.radius
344
+ self.startangle = 0.0
345
+ self.endangle = 0.0
307
346
  self.p1 = self.center[0]-self.radius, self.center[1]
308
347
  self.p2 = self.center[0]+self.radius, self.center[1]
309
348
  self.n1 = None
@@ -388,6 +427,10 @@ class Circle(Shape):
388
427
  def center_of_connection(self, ndec=6):
389
428
  return (self.center[0] + self.radius, self.center[1])
390
429
 
430
+ def maxdist(self, r):
431
+ r0 = np.linalg.norm(self.center)
432
+ return max(abs(r-r0-self.radius), abs(r-r0+self.radius))
433
+
391
434
  def overlapping_shape(self, e, rtol=1e-03, atol=1e-03):
392
435
  if not (isinstance(e, Arc) or isinstance(e, Circle)):
393
436
  # end overlapping_shape: Circle (not Arc or Circle)
@@ -445,7 +488,10 @@ class Circle(Shape):
445
488
  d = distance(self.center, p)
446
489
 
447
490
  if np.isclose(d, self.radius, rtol, atol):
448
- if line.is_point_inside(p, rtol, atol, include_end):
491
+ if line.is_point_inside(p,
492
+ rtol=rtol,
493
+ atol=atol,
494
+ include_end=include_end):
449
495
  # Wenn der Abstand d dem Radius entspricht, handelt es sich um
450
496
  # eine Tangente und es gibt genau einen Schnittpunkt
451
497
  if include_end:
@@ -465,8 +511,14 @@ class Circle(Shape):
465
511
  # Die Schnittpunkte p1 und p2 sind bestimmt. Nun muss noch sicher
466
512
  # gestellt werden, dass sie innerhalb des Start- und Endpunkts der
467
513
  # Linie liegen
468
- p1_inside = line.is_point_inside(p1, rtol, atol, include_end)
469
- p2_inside = line.is_point_inside(p2, rtol, atol, include_end)
514
+ p1_inside = line.is_point_inside(p1,
515
+ rtol=rtol,
516
+ atol=atol,
517
+ include_end=include_end)
518
+ p2_inside = line.is_point_inside(p2,
519
+ rtol=rtol,
520
+ atol=atol,
521
+ include_end=include_end)
470
522
  if p1_inside:
471
523
  if p2_inside:
472
524
  return [p1, p2]
@@ -510,22 +562,60 @@ class Circle(Shape):
510
562
  # let Arc do the work
511
563
  return arc.intersect_circle(self, rtol, atol, include_end)
512
564
 
513
- def is_point_inside(self, p, rtol=1e-03, atol=1e-03, include_end=False):
565
+ def concatenate_arc(self, n1, n2, el,
566
+ rtol=1e-03, atol=1e-03,
567
+ overlapping=False):
568
+ if not points_are_close(self.center, el.center):
569
+ return None
570
+ if not np.isclose(self.radius, el.radius):
571
+ return None
572
+ # it's a circle
573
+ return Circle(Element(center=self.center, radius=self.radius))
574
+
575
+ def concatenate_circle(self, n1, n2, el,
576
+ rtol=1e-03, atol=1e-03,
577
+ overlapping=False):
578
+ if not points_are_close(self.center, el.center):
579
+ return None
580
+ if not np.isclose(self.radius, el.radius):
581
+ return None
582
+ # it's a circle
583
+ return Circle(Element(center=self.center, radius=self.radius))
584
+
585
+ def is_point_inside(self, p,
586
+ rtol=1e-03,
587
+ atol=1e-03,
588
+ include_end=False,
589
+ ignore_end=False,
590
+ mdec=0):
514
591
  """ returns true if p is on circle
515
592
  """
516
593
  d = distance(p, self.center)
517
- return np.isclose(d, self.radius)
594
+ if not np.isclose(d, self.radius, rtol=rtol, atol=atol):
595
+ return False
596
+ if points_are_close(p, self.p1, rtol=rtol, atol=atol):
597
+ return include_end
598
+ elif points_are_close(p, self.p2, rtol=rtol, atol=atol):
599
+ return include_end
600
+ return True
518
601
 
519
- def split(self, points, rtol, atol):
602
+ def split(self, points, rtol=1e-03, atol=1e-03, mdec=0):
520
603
  """ Die Funktion splittet das Circle-Objekt an den vorgegebenen Punkten
521
604
  und gibt eine Liste der neu enstandenen Elemente aus.
522
605
  """
523
- if len(points) == 1:
606
+ if len(points):
524
607
  p = points[0]
525
608
  split_arcs = []
526
609
  alpha1 = alpha_line(self.center, p)
527
- alpha2 = normalise_angle(alpha1 + np.pi/2)
528
- alpha3 = normalise_angle(alpha1 + np.pi)
610
+ if len(points) == 2:
611
+ p = points[1]
612
+ alpha3 = alpha_line(self.center, p)
613
+ alpha2 = middle_angle(alpha1, alpha3)
614
+ alpha4 = middle_angle(alpha3, alpha1)
615
+ else:
616
+ alpha2 = normalise_angle(alpha1 + np.pi/2)
617
+ alpha3 = normalise_angle(alpha1 - np.pi/2)
618
+ alpha4 = None
529
619
 
530
620
  arc = Arc(Element(center=self.center, radius=self.radius,
531
621
  start_angle=alpha1*180/np.pi,
@@ -539,8 +629,17 @@ class Circle(Shape):
539
629
  arc.copy_attributes(self)
540
630
  split_arcs.append(arc)
541
631
 
632
+ if alpha4:
633
+ arc = Arc(Element(center=self.center, radius=self.radius,
634
+ start_angle=alpha3*180/np.pi,
635
+ end_angle=alpha4*180/np.pi))
636
+ arc.copy_attributes(self)
637
+ split_arcs.append(arc)
638
+ else:
639
+ alpha4 = alpha3
640
+
542
641
  arc = Arc(Element(center=self.center, radius=self.radius,
543
- start_angle=alpha3*180/np.pi,
642
+ start_angle=alpha4*180/np.pi,
544
643
  end_angle=alpha1*180/np.pi))
545
644
  arc.copy_attributes(self)
546
645
  split_arcs.append(arc)
@@ -565,6 +664,17 @@ class Circle(Shape):
565
664
  def get_angle_of_arc(self):
566
665
  return np.pi*2.0
567
666
 
667
+ def is_near(self, n):
668
+ if n[0] > self.center[0] + self.radius + 1e-02:
669
+ return False
670
+ if n[0] < self.center[0] - self.radius - 1e-02:
671
+ return False
672
+ if n[1] > self.center[1] + self.radius + 1e-02:
673
+ return False
674
+ if n[1] < self.center[1] - self.radius - 1e-02:
675
+ return False
676
+ return True
677
+
568
678
  def __str__(self):
569
679
  return "Circle c={}, r={}".format(self.center, self.radius)
570
680
 
@@ -656,6 +766,12 @@ class Arc(Circle):
656
766
  x, y = self(midangle)
657
767
  return (round(x, ndec), round(y, ndec))
658
768
 
769
+ def maxdist(self, r):
770
+ a = np.array([np.linalg.norm(self.center),
771
+ np.linalg.norm(self.p1),
772
+ np.linalg.norm(self.p2)])
773
+ return np.max(np.abs(a-r))
774
+
659
775
  def length(self):
660
776
  """returns length of this arc"""
661
777
  d = alpha_angle(self.startangle, self.endangle)
@@ -666,9 +782,9 @@ class Arc(Circle):
666
782
  def get_alpha(self, n):
667
783
  a = alpha_line(n, self.center)
668
784
  if points_are_close(n, self.n1):
669
- alpha1 = normalise_angle(a + np.pi / 2)
785
+ alpha1 = normalise_angle(a + np.pi * 0.5)
670
786
  elif points_are_close(n, self.n2):
671
- alpha1 = normalise_angle(a - np.pi / 2)
787
+ alpha1 = normalise_angle(a - np.pi * 0.5)
672
788
  else:
673
789
  alpha1 = 0.0
674
790
  return alpha1
@@ -701,9 +817,9 @@ class Arc(Circle):
701
817
  return None
702
818
 
703
819
  points = []
704
- if self.is_point_inside(e.p1, rtol, atol):
705
- if self.is_point_inside(e.p2, rtol, atol):
706
- if e.is_point_inside(self.p2, rtol, atol):
820
+ if self.is_point_inside(e.p1, rtol=rtol, atol=atol):
821
+ if self.is_point_inside(e.p2, rtol=rtol, atol=atol):
822
+ if e.is_point_inside(self.p2, rtol=rtol, atol=atol):
707
823
  # a Circle
708
824
  points.append(e.p1)
709
825
  points.append(self.p2)
@@ -719,23 +835,23 @@ class Arc(Circle):
719
835
  points.append(self.p1)
720
836
  points.append(e.p1)
721
837
  points.append(self.p2)
722
- if e.is_point_inside(self.p2, rtol, atol):
838
+ if e.is_point_inside(self.p2, rtol=rtol, atol=atol):
723
839
  points.append(e.p2)
724
840
 
725
- elif self.is_point_inside(e.p2, rtol, atol):
841
+ elif self.is_point_inside(e.p2, rtol=rtol, atol=atol):
726
842
  points.append(e.p1)
727
843
  points.append(self.p1)
728
844
  points.append(e.p2)
729
845
  points.append(self.p2)
730
846
 
731
- elif e.is_point_inside(self.p1, rtol, atol):
847
+ elif e.is_point_inside(self.p1, rtol=rtol, atol=atol):
732
848
  points.append(e.p1)
733
849
  points.append(self.p1)
734
850
  points.append(self.p2)
735
- if e.is_point_inside(self.p2, rtol, atol):
851
+ if e.is_point_inside(self.p2, rtol=rtol, atol=atol):
736
852
  points.append(e.p2)
737
853
 
738
- elif e.is_point_inside(self.p2, rtol, atol):
854
+ elif e.is_point_inside(self.p2, rtol=rtol, atol=atol):
739
855
  if not points_are_close(self.p1, e.p1, rtol=rtol, atol=atol):
740
856
  logger.error("FATAL ERROR in overlapping_shape() of Arc")
741
857
 
@@ -759,12 +875,17 @@ class Arc(Circle):
759
875
  Schnittpunkte bestimmt und in einer Liste ausgegeben
760
876
  """
761
877
  points = super(Arc, self).intersect_line(line, rtol, atol, include_end)
878
+ if not points:
879
+ return []
762
880
 
763
881
  # all possible points have been found
764
882
  # Lets see if they are on a arc
765
883
  remaining_points = []
766
884
  for p in points:
767
- if self.is_point_inside(p, rtol, atol, include_end):
885
+ if self.is_point_inside(p,
886
+ rtol=rtol,
887
+ atol=atol,
888
+ include_end=include_end):
768
889
  remaining_points.append(p)
769
890
  return remaining_points
770
891
 
@@ -779,7 +900,10 @@ class Arc(Circle):
779
900
  # (has been assumed as a circle)
780
901
  remaining_points = []
781
902
  for p in points:
782
- if arc.is_point_inside(p, rtol, atol, include_end):
903
+ if arc.is_point_inside(p,
904
+ rtol=rtol,
905
+ atol=atol,
906
+ include_end=include_end):
783
907
  remaining_points.append(p)
784
908
  return remaining_points
785
909
 
@@ -800,16 +924,21 @@ class Arc(Circle):
800
924
  # Intersection points exist. Take the ones on the arc
801
925
  remaining_points = []
802
926
  for p in points:
803
- if self.is_point_inside(p, rtol, atol, include_end):
927
+ if self.is_point_inside(p,
928
+ rtol=rtol,
929
+ atol=atol,
930
+ include_end=include_end):
804
931
  remaining_points.append(p)
805
932
  return remaining_points
806
933
 
807
- def split(self, points, rtol=1e-03, atol=1e-03):
934
+ def split(self, points, rtol=1e-03, atol=1e-03, mdec=0):
808
935
  """ return a list of arcs by splitting
809
936
  """
810
937
  points_inside = [p
811
938
  for p in points
812
- if self.is_point_inside(p, rtol, atol, False)]
939
+ if self.is_point_inside(p,
940
+ rtol=rtol,
941
+ atol=atol)]
813
942
  if len(points_inside) == 1:
814
943
  p = points_inside[0]
815
944
  split_arcs = []
@@ -844,31 +973,89 @@ class Arc(Circle):
844
973
  end_angle=self.endangle*180/np.pi))
845
974
  return a1, a2
846
975
 
847
- def concatenate_arc(self, n1, n2, el):
976
+ def concatenate_arc(self, n1, n2, el,
977
+ rtol=1e-03, atol=1e-03,
978
+ overlapping=False):
848
979
  if not points_are_close(self.center, el.center):
849
980
  return None
850
981
  if not np.isclose(self.radius, el.radius):
851
982
  return None
852
983
 
853
- if np.isclose(self.startangle, el.endangle):
854
- start_angle = el.startangle
855
- end_angle = self.endangle
856
- elif np.isclose(el.startangle, self.endangle):
857
- start_angle = self.startangle
858
- end_angle = el.endangle
859
- else:
860
- return None
984
+ my_start = normalise_angle(self.startangle)
985
+ my_end = normalise_angle(self.endangle)
986
+ el_start = normalise_angle(el.startangle)
987
+ el_end = normalise_angle(el.endangle)
988
+
989
+ logger.debug("begin of concatenate_arc")
990
+ logger.debug("my startangle: %s, endangle: %s", my_start, my_end)
991
+ logger.debug("el startangle: %s, endangle: %s", el_start, el_end)
992
+
993
+ start_angle = None
994
+ end_angle = None
995
+
996
+ if np.isclose(my_start, el_end, rtol=rtol, atol=atol):
997
+ if is_angle_inside(my_start, my_end, el_start): # Circle
998
+ logger.debug("1: new startangle is 0 (Circle)")
999
+ start_angle = 0.0
1000
+ end_angle = 0.0
1001
+ else:
1002
+ logger.debug("1: new startangle is el: %s", el_start)
1003
+ start_angle = el_start
1004
+ end_angle = my_end
1005
+ elif np.isclose(el_start, my_end, rtol=rtol, atol=atol):
1006
+ logger.debug("2: new startangle is my: %s", my_start)
1007
+ start_angle = my_start
1008
+ end_angle = el_end
1009
+
1010
+ if start_angle is None:
1011
+ if overlapping:
1012
+ if is_angle_inside(my_start, my_end, el_start):
1013
+ logger.debug("3: new startangle is my: %s", my_start)
1014
+ start_angle = my_start
1015
+ elif is_angle_inside(el_start, el_end, my_start):
1016
+ logger.debug("4: new startangle is el: %s", el_start)
1017
+ start_angle = el_start
1018
+ if start_angle is None:
1019
+ return None
1020
+
1021
+ if is_angle_inside(my_start, my_end, el_end):
1022
+ end_angle = my_end
1023
+ elif is_angle_inside(el_start, el_end, my_end):
1024
+ end_angle = el_end
1025
+ if end_angle is None:
1026
+ return None
1027
+ else:
1028
+ return None
861
1029
 
862
- logger.debug("concatenate_arc: start=%s, end=%s",
863
- start_angle,
864
- end_angle)
1030
+ logger.debug("new startangle: %s, endangle: %s", start_angle, end_angle)
1031
+
1032
+ if np.isclose(start_angle, end_angle):
1033
+ # it's a circle
1034
+ return Circle(Element(center=self.center, radius=self.radius))
1035
+
1036
+ logger.debug("end of concatenate_arc")
865
1037
  return Arc(Element(center=self.center,
866
1038
  radius=self.radius,
867
1039
  start_angle=start_angle,
868
1040
  end_angle=end_angle),
869
1041
  rf=1)
870
1042
 
871
- def is_point_inside(self, p, rtol=1e-03, atol=1e-03, include_end=False):
1043
+ def concatenate_circle(self, n1, n2, el,
1044
+ rtol=1e-03, atol=1e-03,
1045
+ overlapping=False):
1046
+ if not points_are_close(self.center, el.center):
1047
+ return None
1048
+ if not np.isclose(self.radius, el.radius):
1049
+ return None
1050
+ # it's a circle
1051
+ return Circle(Element(center=self.center, radius=self.radius))
1052
+
1053
+ def is_point_inside(self, p,
1054
+ rtol=1e-03,
1055
+ atol=1e-03,
1056
+ include_end=False,
1057
+ ignore_end=False,
1058
+ mdec=0):
872
1059
  """ returns true if p is on arc
873
1060
  """
874
1061
  # logger.debug("is_point_inside: p=%s", p)
@@ -1114,6 +1301,11 @@ class Line(Shape):
1114
1301
  y = (self.p1[1]+self.p2[1])/2
1115
1302
  return (x, y)
1116
1303
 
1304
+ def maxdist(self, r):
1305
+ r1 = np.linalg.norm(self.p1)
1306
+ r2 = np.linalg.norm(self.p2)
1307
+ return max([abs(r1-r), abs(r2-r)])
1308
+
1117
1309
  def length(self):
1118
1310
  return np.sqrt(self.dx()**2 + self.dy()**2)
1119
1311
 
@@ -1121,6 +1313,9 @@ class Line(Shape):
1121
1313
  px = self.center_of_connection()
1122
1314
  return alpha_line(px, n)
1123
1315
 
1316
+ def get_positive_angle(self):
1317
+ return elevation_angle(alpha_line(self.p1, self.p2))
1318
+
1124
1319
  def range(self, step=1.0):
1125
1320
  """returns evenly spaced values"""
1126
1321
  num = max(int(self.length()/step), 1)
@@ -1183,8 +1378,14 @@ class Line(Shape):
1183
1378
  if all:
1184
1379
  return[point]
1185
1380
 
1186
- if line.is_point_inside(point, rtol, atol, include_end):
1187
- if self.is_point_inside(point, rtol, atol, include_end):
1381
+ if line.is_point_inside(point,
1382
+ rtol=rtol,
1383
+ atol=atol,
1384
+ include_end=include_end):
1385
+ if self.is_point_inside(point,
1386
+ rtol=rtol,
1387
+ atol=atol,
1388
+ include_end=include_end):
1188
1389
  return [point]
1189
1390
  return []
1190
1391
 
@@ -1201,15 +1402,15 @@ class Line(Shape):
1201
1402
  """
1202
1403
  return circle.intersect_line(self, rtol, atol, include_end)
1203
1404
 
1204
- def split(self, points, rtol=1e-03, atol=1e-03):
1405
+ def split(self, points, rtol=1e-03, atol=1e-03, mdec=0):
1205
1406
  """ Die Funktion splittet das Line-Objekt an den vorgegebenen Punkten
1206
1407
  und gibt eine Liste der neu enstandenen Elemente aus.
1207
1408
  """
1208
1409
  points_inside = [(distance(p, self.p1), p)
1209
1410
  for p in points if self.is_point_inside(p,
1210
- rtol, atol,
1211
- False)]
1212
-
1411
+ rtol=rtol,
1412
+ atol=atol,
1413
+ mdec=mdec)]
1213
1414
  if len(points_inside) > 0:
1214
1415
  points_inside.append((0.0, self.p1))
1215
1416
  points_inside.append((distance(self.p1, self.p2), self.p2))
@@ -1235,45 +1436,107 @@ class Line(Shape):
1235
1436
  l2 = Line(Element(start=pm, end=self.p2))
1236
1437
  return l1, l2
1237
1438
 
1238
- def concatenate_line(self, n1, n2, el):
1239
- if not np.isclose(self.m(999999.0), el.m(999999.0)):
1439
+ def concatenate_line(self, n1, n2, el,
1440
+ rtol=1e-05, atol=1e-05,
1441
+ mdec=0,
1442
+ overlapping=False):
1443
+ none_val = 9999999.0
1444
+ my_m = self.m(none_val, dec=mdec)
1445
+ el_m = el.m(none_val, dec=mdec)
1446
+ #logger.debug("concatenate_line: my m=%s, el m=%s", my_m, el_m)
1447
+ if not np.isclose(my_m, el_m):
1448
+ #logger.debug("concatenate_line #1: m %s and %s are not close", my_m, el_m)
1240
1449
  return None
1241
-
1242
1450
  if n1 and n2:
1451
+ #logger.debug("concatenate_line #2: Nodes %s and %s", n1, n2)
1243
1452
  return Line(Element(start=n1, end=n2))
1244
1453
 
1245
- if points_are_close(self.p1, el.p1):
1246
- return Line(Element(start=self.p2, end=el.p2))
1247
- if points_are_close(self.p1, el.p2):
1248
- return Line(Element(start=self.p2, end=el.p1))
1249
- if points_are_close(self.p2, el.p1):
1250
- return Line(Element(start=self.p1, end=el.p2))
1251
- if points_are_close(self.p2, el.p2):
1252
- return Line(Element(start=self.p1, end=el.p1))
1454
+ my_p1, my_p2 = self.points_sorted(rtol=rtol, atol=atol)
1455
+ el_p1, el_p2 = el.points_sorted(rtol=rtol, atol=atol)
1456
+
1457
+ logger.debug("my p1: %s, p2: %s", my_p1,my_p2)
1458
+ logger.debug("el p1: %s, p2: %s", el_p1,el_p2)
1459
+
1460
+ if points_are_close(my_p2, el_p1, rtol=rtol, atol=atol):
1461
+ return Line(Element(start=my_p1, end=el_p2))
1462
+ if points_are_close(my_p1, el_p2, rtol=rtol, atol=atol):
1463
+ return Line(Element(start=el_p1, end=my_p2))
1464
+
1465
+ if overlapping:
1466
+ if points_are_close(my_p1, el_p1, rtol=rtol, atol=atol):
1467
+ my_d = distance(my_p1, my_p2)
1468
+ el_d = distance(el_p1, el_p2)
1469
+ if el_d > my_d:
1470
+ return Line(Element(start=el_p1, end=el_p2))
1471
+ else:
1472
+ return Line(Element(start=my_p1, end=my_p2))
1473
+
1474
+ if points_are_close(my_p2, el_p2, rtol=rtol, atol=atol):
1475
+ my_d = distance(my_p1, my_p2)
1476
+ el_d = distance(el_p1, el_p2)
1477
+ if el_d > my_d:
1478
+ return Line(Element(start=el_p1, end=el_p2))
1479
+ else:
1480
+ return Line(Element(start=my_p1, end=my_p2))
1481
+
1482
+ start = None
1483
+ if self.is_point_inside(el_p1,
1484
+ rtol=rtol,
1485
+ atol=atol,
1486
+ include_end=True,
1487
+ mdec=mdec):
1488
+ start = my_p1
1489
+ elif el.is_point_inside(my_p1,
1490
+ rtol=rtol,
1491
+ atol=atol,
1492
+ include_end=True,
1493
+ mdec=mdec):
1494
+ start = el_p1
1495
+ if start is None:
1496
+ logger.debug("concatenate_line #4: no start")
1497
+ return None
1498
+ end = None
1499
+ if self.is_point_inside(el_p2,
1500
+ rtol=rtol,
1501
+ atol=atol,
1502
+ include_end=True,
1503
+ mdec=mdec):
1504
+ end = my_p2
1505
+ elif el.is_point_inside(my_p2,
1506
+ rtol=rtol,
1507
+ atol=atol,
1508
+ include_end=True,
1509
+ mdec=mdec):
1510
+ end = el_p2
1511
+ if end is None:
1512
+ logger.debug("concatenate_line #4: no end")
1513
+ return None
1514
+
1515
+ return Line(Element(start=start, end=end))
1516
+
1253
1517
  return None
1254
1518
 
1255
1519
  def is_point_inside(self, point,
1256
1520
  rtol=1e-03,
1257
1521
  atol=1e-03,
1258
- include_end=False):
1259
- """ returns True if point is between start and end point
1522
+ include_end=False,
1523
+ ignore_end=False,
1524
+ mdec=0):
1525
+ """ returns True if point is between start and end point of the line
1260
1526
  """
1261
- if points_are_close(point, self.p1, rtol, atol):
1262
- return include_end
1263
- if points_are_close(point, self.p2, rtol, atol):
1264
- return include_end
1265
-
1266
- m1 = line_m(self.p1, point)
1267
- m2 = line_m(point, self.p2)
1268
-
1269
- if m1 is None or m2 is None:
1270
- # check x-Values
1271
- if np.isclose(self.p1[0], point[0]) and np.isclose(self.p2[0], point[0]):
1272
- m1 = None
1273
- m2 = None
1274
- if m1 is not None or m2 is not None:
1275
- return False
1276
- elif not np.isclose(m1, m2, rtol, atol):
1527
+ if not ignore_end:
1528
+ if points_are_close(point, self.p1, rtol, atol):
1529
+ return include_end
1530
+ if points_are_close(point, self.p2, rtol, atol):
1531
+ return include_end
1532
+
1533
+ elevation_tol = np.pi / 360 / 8 # 1/8 degree
1534
+ elevation_line = elevation_angle(alpha_line(self.p1, self.p2))
1535
+ elevation_point1 = elevation_angle(alpha_line(self.p1, point))
1536
+ elevation_point2 = elevation_angle(alpha_line(point, self.p2))
1537
+
1538
+ if not (np.isclose(elevation_line, elevation_point1, rtol=rtol, atol=atol) or \
1539
+ np.isclose(elevation_line, elevation_point2, rtol=rtol, atol=atol)):
1277
1540
  return False
1278
1541
 
1279
1542
  length = distance(self.p1, self.p2)
@@ -1299,7 +1562,9 @@ class Line(Shape):
1299
1562
  n = line_n(self.p1, m)
1300
1563
  p = intersect_point(center, self.p1, m, n)
1301
1564
 
1302
- if self.is_point_inside(p, 1e-03, 1e-03):
1565
+ if self.is_point_inside(p,
1566
+ rtol=1e-03,
1567
+ atol=1e-03):
1303
1568
  dist_min = min(distance(center, p), dist_min)
1304
1569
 
1305
1570
  return (dist_min, dist_max)
@@ -1314,9 +1579,10 @@ class Line(Shape):
1314
1579
  else:
1315
1580
  alpha_p2 = alpha_line(center, self.p2)
1316
1581
  if alpha_p1 is None:
1317
- alpha_p1 = alpha_p2
1582
+ assert(alpha_p2 is not None)
1583
+ return (alpha_p2, alpha_p2)
1318
1584
  if alpha_p2 is None:
1319
- alpha_p2 = alpha_p1
1585
+ return (alpha_p1, alpha_p1)
1320
1586
  if alpha_angle(alpha_p1, alpha_p2) < np.pi:
1321
1587
  return (alpha_p1, alpha_p2)
1322
1588
  else:
@@ -1331,6 +1597,17 @@ class Line(Shape):
1331
1597
  def get_angle_of_arc(self):
1332
1598
  return 0.0
1333
1599
 
1600
+ def is_near(self, n):
1601
+ if n[0] > max(self.p1[0], self.p2[0]) + 1e-02:
1602
+ return False
1603
+ if n[0] < min(self.p1[0], self.p2[0]) - 1e-02:
1604
+ return False
1605
+ if n[1] > max(self.p1[1], self.p2[1]) + 1e-02:
1606
+ return False
1607
+ if n[1] < min(self.p1[1], self.p2[1]) - 1e-02:
1608
+ return False
1609
+ return True
1610
+
1334
1611
  def __str__(self):
1335
1612
  return "Line p1={}, p2={}, n1={}, n2={}".format(self.p1,
1336
1613
  self.p2,