2
# -*- coding: utf-8 -*-
4
# LORZE erasandcad, a 2D CAD with an intuitive user interface, simple and easy.
5
# http://erasand.jimdo.com/python-programme/lorze/
6
# (C) 2012, Andreas Ulrich
8
# This file is part of “LORZE erasandcad“
10
# “LORZE erasandcad“ is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
12
# “LORZE erasandcad“ is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14
# You should have received a copy of the GNU General Public License along with LORZE erasandcad. If not, see <http://www.gnu.org/licenses/>.
20
# Geometry helper object for LORZE erasandcad
22
def __init__(self, options):
24
self.__options= options
26
def RectInRect(self, rect1, rect2):
27
# check is rectangle in rectangle, return True of False, r1= (r1x, r1y, width1, height1), r2= (r2x, r2y, width2, height2), read the values
28
r1x, r1y, r1w, r1h= rect1
29
r2x, r2y, r2w, r2h= rect2
31
# half width and height
37
# distance between centers
38
dx= abs((r1x+ r1w2)- (r2x+ r2w2))
39
dy= abs ((r1y+ r1h2)- (r2y+ r2h2))
41
if dx> (r1w2+ r2w2) or dy> (r1h2+ r2h2):
42
# the distance between the centers of the rectangles ar too big
45
# the rectangles ar inside each other
49
def PointInRect(self, point, rect):
50
# check is point in rectangle, return True or False, point= (px, py), rect= (rx, ry, width2, height2), read the values
55
if px>= rx and px<= rx2 and py>= ry and py<= ry2:
56
# point is in the rechtanlge
59
# point is not in the rectangle
63
def PointOverSegment(self, segment, point, tolerance):
64
# check is point over segment, return coordinate, (x, y) or None, segment= (sx1, sy1, sx2, r2y), point= (px, py), tolerance= value / read the values
65
sx1, sy1, sx2, sy2= segment
71
# get rectangle coordinates of the segment, height can be positive or negative
72
sx, sy, sw, sh= self.LineXYtoWH(sx1, sy1, sx2, sy2)
76
if sx>= (px- tolerance) and sx<= (px+ tolerance):
78
if sh>= 0 and sy<= py and (sy+ sh)>= py:
79
# point on segment found with positive height, return point
81
if sh< 0 and sy>= py and (sy+ sh)<= py:
82
# point on segment found with negative height, return point
87
if sy>= (py- tolerance) and sy<= (py+ tolerance):
89
if sx<= px and (sx+ sw)>= px:
90
# point on segment found, return point
94
# oblique line / move segment to zero, correct point with the same amount
98
#ratio height to width
103
if px>= 0 and px<= sw:
104
# point is over segment, height of the point
106
if ph>= (py- tolerance) and ph<= (py+ tolerance):
109
value= (px+ sx, foundy+ sy)
113
if py>= 0 and py<= sh:
114
# point is over segment, height of the point
116
if pw>= (px- tolerance) and pw<= (px+ tolerance):
119
value= (foundx+ sx, py+ sy)
124
def RectXYtoWH(self, x1, y1, x2, y2):
125
# convert a rectangle x1, y1, x2, y2 to a rectangle, x, y, w, h
144
def LineXYtoWH(self, x1, y1, x2, y2):
145
# oblique line, move line at left point to zero point, move point with the same amount
147
# correct line, 1st point is on the right side, width & height segment
150
# 1st point on the left side
153
# dont correct line, 1st point is on the left side, width & height segment
156
# 1st point on the left side
162
def GetKar(self, angle, length):
163
angle, length= float(angle), float(length) # set angle and length: float
165
angle= self.CheckAngle(angle) # check over- underflow
167
# get width and height from angle and length: float
171
a= math.radians(angle)
172
w= math.cos(a)* length
173
h= math.sin(a)* length
174
width= round(w, self.__options.GetDezPlcs())
175
height= round(h, self.__options.GetDezPlcs())
176
return (width, height)
178
def GetPol(self, width, height):
179
# set width and height: float
180
width, height= float(width), float(height)
182
# get angle and length from width and height: float
183
if width== 0 and height== 0:
185
elif width== 0 and height> 0:
188
elif width== 0 and height< 0:
191
elif width> 0 and height== 0:
194
elif width< 0 and height== 0:
200
a= math.degrees(math.atan(h/w))
201
l= math.sqrt(h**2+ w**2)
202
if height> 0 and width< 0:
204
elif height< 0 and width< 0:
206
elif height<0 and width> 0:
208
angle= round(a, self.__options.GetDezPlcs())
209
length= round(l, self.__options.GetDezPlcs())
210
return (angle, length)
213
# width= a, height= g, length= h, angle= w
214
# sin(w)= g/ h, g= sin(w)* h, h= g/ sin(w)
215
# cos(w)= a/ h, a= cos(w)* h, h= a/ cos(w)
216
# tan(w)= g/ a, g= tan(w)* a, a= g/ tan(w)
219
def PointsMove(self, pointlist, dx, dy):
220
# pointlist: [(x, y), (x, y), ..], dx, dy vector to move
226
result.append((x, y))
230
def PointsMirror(self, pointlist, xaxis, yaxis):
231
# pointlist: [(x, y), (x, y), ..], xaxis, yaxis: axis to mirror, None for not mirror with this axis, get mirrored points (list)
239
result.append((x, y))
243
def PointsRot(self, pointlist, centx, centy, angle):
244
# pointlist: [(x, y), (x, y), ..], anlge as float
247
# check over- underflow
248
angle= self.CheckAngle(angle)
250
# select rotation and return result
254
elif angle== 180 or angle== -180:
255
# get points with 180 degree rotated
259
x= centx+ ((x-centx)* -1)
260
y= centy+ ((y-centy)* -1)
261
result.append((x, y))
263
elif angle== 90 or angle== -90:
264
# get points with 90 degree rotated
266
d= angle/ 90 # +90= 1, -90= -1
269
xnew= centx+ ((y- centy)* d* -1)
270
ynew= centy+ ((x- centx)* d)
271
result.append((xnew, ynew))
273
elif angle== 270 or angle== -270:
274
# get points with 270 degree rotated
276
d= angle/ 270 # +270= 1, -270= -1
279
xnew= centx+ ((y- centy)* d)
280
ynew= centy+ ((x- centx)* d* -1)
281
result.append((xnew, ynew))
283
# get points with another angle rotated
285
a= math.radians(angle)
290
x= centx+ (dx)* math.cos(a) - (dy)* math.sin(a)
291
y= centy+ (dx)* math.sin(a) + (dy)* math.cos(a)
292
result.append((round(x, self.__options.GetDezPlcs()), round(y, self.__options.GetDezPlcs())))
296
# Entwicklung der 90 Grad Drehformel
297
# x+40, y 0, +90(x 0, y+40), -90(x 0, y-40), x=y*d*-1 , y=x*d
298
# x 0, y+40, +90(x-40, y 0), -90(x+40, y 0), x=y*d*-1 , y=x*d
299
# x-40, y 0, +90(x 0, y-40), -90(x 0, y+40), x=y*d*-1 , y=x*d
300
# x 0, y-40, +90(x+40, y 0), -90(x-40, y 0), x=y*d*-1 , y=x*d
301
# x+20, y+10, +90(x-10, y+20), -90(x+10, y-20), x=y*d*-1, y= x*d
302
# x-10, y+20, +90(x-20, y-10), -90(x+20, y+10), x=y*d*-1, y= x*d
303
# x-20, y-10, +90(x+10, y-20), -90(x-10, y+20), x=y*d*-1, y= x*d
304
# x+10, y-20, +90(x+20, y+10), -90(x-20, y-10), x=y*d*-1, y= x*d
306
# Formel 90 Grad Drehung
307
# xnew= y- cy #Koordinaten tauschen, relativ zum Drehzentrum
308
# xnew= xnew* d* -1 #Vorzeichen anpassen
309
# xnew= cx+ xnew #Vom Drehzentrum zurückschieben
310
# ynew= x- cx #Koordinaten tauschen, relativ zum Drehzentrum
311
# ynew= ynew* d #Vorzeichen anpassen
312
# ynew= cy+ ynew #Vom Drehzentrum zurückschieben
317
# xnew= dx* math.cos(a) - dy* math.sin(a)
318
# ynew= dx* math.sin(a) + dy* math.cos(a)
323
def CheckAngle(self, angle):
324
# check over- & underflow
330
angle= (abs(angle)% 360)* -1
334
def PointsScale(self, pointlist, centx, centy, factx, facty):
336
factx, facty= float(factx), float(facty)
342
x= centx+ ((x- centx)* factx)
343
y= centy+ ((y- centy)* facty)
344
result.append((round(x, self.__options.GetDezPlcs()), round(y, self.__options.GetDezPlcs())))
348
def IntersSegm(self, segment1, segmentlist):
349
# find all intersections of a segment (x1, y1, x2, y2) and a segmentlist [(x1, y1, x2, y2), ..], return point- flaglist [(x, y, flag), ..], flag for True or False ist the intersection over the first segment
351
# read segment1 coordinates
352
x1, y1, x2, y2= segment1
354
# get rectangle coordinates of the segment, height can be positive or negative
355
x, y, w, h= self.LineXYtoWH(x1, y1, x2, y2)
357
# polar coordinates of segment 1
358
a, l= self.GetPol(w, h)
360
# angke correction, rotate segment to 90 degrees
361
corrangle= (a* -1)+ 90
364
# objects has to be rotatet / generate pointlist from segment1 & segmentlist
365
pointlist= [(x1, y1), (x2, y2)]
366
for i in segmentlist:
367
pointlist.append((i[0], i[1]))
368
pointlist.append((i[2], i[3]))
370
# rotate at point 0, 0
371
pointsnew= self.PointsRot(pointlist, 0, 0, corrangle)
373
# generate segment1 & segmentlist from pointlist
374
x1, y1, x2, y2= pointsnew[0][0], pointsnew[0][1], pointsnew[1][0], pointsnew[1][1]
376
for i in range(2, len(pointsnew), 2):
377
segmentlist.append((pointsnew[i][0], pointsnew[i][1], pointsnew[i+ 1][0], pointsnew[i+ 1][1]))
379
# rect of the segment 1
380
x, y, w, h= self.LineXYtoWH(x1, y1, x2, y2)
382
# set empty poinflaglist
385
for i in segmentlist:
386
# read segment coordinates, rectangle format
387
sx, sy, sw, sh= self.LineXYtoWH(i[0], i[1], i[2], i[3])
397
if foundx>= sx and foundx<= (sx+ sw):
403
# oblique line / calculate from x, y of the segment, correct x segment1
406
#ratio height to width
409
# calculate intersection
410
foundy= (xcorr* sa)+ sy
413
if h> 0 and foundy>= y and foundy<= y+ h:
414
# intersection point is over segment1
416
elif h< 0 and foundy<= y and foundy>= y+ h:
417
# intersection point is over segment1
420
# intersection point is not over segment1
424
pointflaglist.append((foundx, foundy, foundflag))
427
# recorrect rotated objects / create pointlist and flaglist
428
pointlist, flaglist= [], []
430
for i in pointflaglist:
431
# generate pointlist and flaglist
432
pointlist.append((i[0], i[1]))
433
flaglist.append(i[2])
435
# rotate at point 0, 0
436
pointsnew= self.PointsRot(pointlist, 0, 0, corrangle* -1)
438
# generate pointsflaglist from pointsnew and flaglist
440
for i in range(len(flaglist)):
441
pointflaglist.append((pointsnew[i][0], pointsnew[i][1], flaglist[i]))
443
return(pointflaglist)
446
if __name__== '__main__':
447
from lrzoptions import LorzeOptions
449
options= LorzeOptions()
450
geohelp= LorzeGeoHelp(options)
452
print('test RectInRect')
453
print('---------------')
454
print('should be False ..', geohelp.RectInRect((10, 10, 20, 20), (35, 35, 10, 10)))
455
print('should be True ..', geohelp.RectInRect((10, 10, 20, 20), (-5, -5, 20, 20)))
456
print('should be False ..', geohelp.RectInRect((-30, -10, 40, 50), (-33, 40, 2, 80)))
457
print('should be True ..', geohelp.RectInRect((50, -10, 40, 1), (90, -40, 30, 30)))
460
print('test PointInRect')
461
print('----------------')
462
print('should be False ..', geohelp.PointInRect((5, 5), (2, 2, 4, 1)))
463
print('should be True ..', geohelp.PointInRect((-7, 5), (-8, 4, 2, 2)))
466
print('test RectXYtoWH')
467
print('------------------')
468
print('should be 10, 10, 30, 20 ..', geohelp.RectXYtoWH(10, 10, 40, 30))
469
print('should be 5, 10, 10, 70 ..', geohelp.RectXYtoWH(15, 80, 5, 10))
470
print('should be 20, -20, 15, 110 ..', geohelp.RectXYtoWH(20, 90, 35, -20))
471
print('should be -90, -12, 80, 10 ..', geohelp.RectXYtoWH(-10, -12, -90, -2))
474
print('Test PointOverSegment')
475
print('---------------------')
476
# horizontal positive
477
print('should be 12, 10 ..', geohelp.PointOverSegment((10, 10, 20, 10), (12, 12), 3))
478
print('should be None ..', geohelp.PointOverSegment((10, 10, 20, 10), (12, 14), 3))
479
# horziontal negative
480
print('should be -15, -10 ..', geohelp.PointOverSegment((-10, -10, -20, -10), (-15, -9), 2))
481
print('should be None ..', geohelp.PointOverSegment((-10, -10, -20, -10), (-7, -9), 2))
483
print('should be -10, -3 ..', geohelp.PointOverSegment((-10, 10, -10, -10), (-7, -3), 4))
484
print('should be None ..', geohelp.PointOverSegment((-10, 10, -10, -10), (0, -15), 4))
486
print('should be 20, -15 ..', geohelp.PointOverSegment((20, -5, 20, -15), (19, -15), 4))
487
print('should be None ..', geohelp.PointOverSegment((20, -5, 20, -15), (19, -16), 4))
489
print('should be 5, 5 ..', geohelp.PointOverSegment((0, 0, 10, 10), (5, 3), 2))
490
print('should be None ..', geohelp.PointOverSegment((0, 0, 10, 10), (5, 2), 2))
492
print('should be 28, -38 ..', geohelp.PointOverSegment((20, -40, 40, -35), (28, -40), 2))
493
print('should be None ..', geohelp.PointOverSegment((20, -40, 40, -35), (28, -41), 2))
495
print('should be -8, 42 ..', geohelp.PointOverSegment((10, -30, -10, 50), (-8, 40), 2))
496
print('should be None ..', geohelp.PointOverSegment((10, -30, -10, 50), (-9, 40), 2))
501
for t in [(0, 256, '256, 0'), (65, 269, '113.684312408, 243.796794713'), (90, 326, '0, 326'), (148, 728, '-617.379014002, 385.781224362'), (180, 369, '-369, 0'), (259, 236, '-45.030922909, -231.664015294'), (270, 268, '0, -268'), (326, 687, '569.548812345, -384.165524684'), (360, 265, '265, 0'), (785, 584, '246.809064857, 529.283747629'), (-53, 59, '35.507086366, -47.119495093'), (-90, 12, '0, -12'), (-159, 25, '-23.339510662, -8.959198739'), (-180, 35, '-35, 0'), (-218, 37, '-29.156397883, 22.779474587'), (-270, 26, '0, 26'), (-289, 27, '8.79034017, 25.529001541'), (-360, 59, '59, 0'), (-489, 43, '-27.060776815, -33.417276343')]:
503
print('should be',control)
504
print('it is ', geohelp.GetKar(a, l))
509
for t in [(26, 0, '0, 26'), (65, 87, '53.235619324, 108.600184162'), (0, 69, '90, 69'), (-58, 56, '136.005086005, 80.622577483'), (-23, 0, '180, 23'), (-45, -39, '220.91438322, 59.548299724'), (0, -12, '270, 12'), (89, -28, '342.536073108, 93.300589494'), (256, 0, '0, 256'), (113.684312408, 243.796794713, '65, 269'), (0, 326, '90, 326'), (-617.379014002, 385.781224362, '148, 728'), (-369, 0, '180, 369'), (-45.030922909, -231.664015294, '259, 236'), (0, -268, '270, 268'), (569.548812345, -384.165524684, '326, 687'), (265, 0, '0, 265'), (246.809064857, 529.283747629, '65, 584'), (35.507086366, -47.119495093, '307, 59'), (0, -12, '270, 12'), (-23.339510662, -8.959198739, '201, 25'), (-35, 0, '180, 35'), (-29.156397883, 22.779474587, '142, 37'), (0, 26, '90, 26'), (8.79034017, 25.529001541, '71, 27'), (59, 0, '0, 59'), (-27.060776815, -33.417276343, '231, 43')]:
511
print('should be',control)
512
print('it is ', geohelp.GetPol(w, h))
515
print('Test PointsMove')
516
print('---------------')
517
pointlist=[(0, 0), (1, 1), (1, -1), (-1, -1), (-1, 1)]
519
for i in [(1, 1, '(1, 1), (2, 2), (2, 0), (0, 0), (0, 2)'), (1, -1, '(1, -1), (2, 0), (2, -2), (0, -2), (0, 0)'), (-1, -1, '(-1, -1), (0, 0), (0, -2), (-2, -2), (-2, 0)'), (-1, 1, '(-1, 1), (0, 2), (0, 0), (-2, 0), (-2, 2)')]:
521
print('should be', control)
522
print('it is ', geohelp.PointsMove(pointlist, x, y))
525
print('Test PointsMirror')
526
print('-----------------')
527
pointlist=[(19, 17), (28, 64), (65, 59), (74, 30)]
528
for i in [(0, 0, '(-19, -17), (-28, -64), (-65, -59), (-74, -30)'), (0, None, '(-19, 17), (-28, 64), (-65, 59), (-74, 30)'), (None, 0, '(19, -17), (28, -64), (65, -59), (74, -30)'), (10, None, '(1, 17), (-8, 64), (-45, 59), (-54, 30)'), (None, -20, '(19, -57), (28, -104), (65, -99), (74, -70)')]:
530
print('should be', control)
531
print('it is ', geohelp.PointsMirror(pointlist, x, y))
534
print('Test PointsRot')
535
print('--------------')
536
liste= [(0, [(25, 31)], 0, 0, '(25.0, 31.0'), (37, [(-48, 27), (25, 31)], -33, 12,
537
'(-54.006758, 14.952307), (1.886374, 62.079346)'), (90, [(-3, -78)], -45, -99, '(-66.0, -57.0)'), (124, [(46, -2), (-3, -78)], -32, 67, '(-18.413454, 170.249241), (71.993854, 172.125061)'), (180, [(89, 0)], 0, 67, '(-89.0, 134.0)'), (212, [(0, 17), (-87, 0)], -53, 0, '(-88.937921, -42.502538), (-24.166365, 18.017255)'), (270, [(-87, 0)], 0, -65, '(65.0, 22.0)'), (303, [(0, -56), (-68, -68)], 13, 0, '(-41.045859, -19.597068), (-88.14536, 30.896862)'), (360, [(-68, -68)], -34, -34, '(-68.0, -68.0)'), (769, [(456, -789)], -6789, 3456, '(1167.889833, 6138.900331)'), (-360, [(25, 31)], 0, 0, '(25.0, 31.0)'), (-323, [(-48, 27), (25, 31)], -33, 12, '(-54.006758, 14.952307), (1.886374, 62.079346)'), (-270, [(-3, -78)], -45, -99, '(-66.0, -57.0)'), (-216, [(46, -2), (-3, -782)], -32, 67, '(-54.546143, 168.669423), (443.568186, 770.9012)'), (-180, [(89, 0)], 0, 67, '(-89.0, 134.0)'), (-148, [(0, 17), (-87, 0)], -53, 0, '(-88.937921, -42.502538), (-24.166365, 18.017255)'), (-90, [(-87, 0)], 0, -65, '(65.0, 22.0)'), (-57, [(0, -56), (-68, -68)], 13, 0, '(-41.045859, -19.597068), (-88.14536, 30.896862)'), (0, [(-68, -68)], -34, -34, '(-68.0, -68.0)'), (-1031, [(456, -789)], -6789, 3456, '(1167.889833, 6138.900331)')]
539
angle, pointlist, cx, cy, control= i
540
print('should be', control)
541
print('it is ', geohelp.PointsRot(pointlist, cx, cy, angle))
544
print('Test PointsScale')
545
print('----------------')
546
pointlist=[(0, 0), (10, 10), (20, -30), (-40, -50), (-60, 70)]
547
for i in [(1, 1, 0, 0, '(0.0, 0.0), (10.0, 10.0), (20.0, -30.0), (-40.0, -50.0), (-60.0, 70.0)'), (2, 3, 4, 5, '(-4.0, -10.0), (16.0, 20.0), (36.0, -100.0), (-84.0, -160.0), (-124.0, 200.0)'), (0.25, 0.5, -2, -4, '(-1.5, -2.0), (1.0, 3.0), (3.5, -17.0), (-11.5, -27.0), (-16.5, 33.0)')]:
548
fx, fy, cx, cy, control= i
549
print('should be', control)
550
print('it is ', geohelp.PointsScale(pointlist, cx, cy, fx, fy))
555
print('10, 8, 20, 10 should be 10, 8, 10, 2 / it is', geohelp.LineXYtoWH(10, 8, 20, 10))
556
print('-2, -6, -20, 30 should be -20, 30, 18, -36 / it is', geohelp.LineXYtoWH(-2, -6, -20, 30))
557
print('-3, -3, -30, -20 should be -30, -20, 27, 17 / it is', geohelp.LineXYtoWH(-3, -3, -30, -20))
558
print('6, -4, 50, -16 should be 6, -4, 44, -12 / it is', geohelp.LineXYtoWH(6, -4, 50, -16))
559
x, y, w, h= geohelp.LineXYtoWH(10, 0, 10, 20)
560
a, l= geohelp.GetPol(w, h)
561
print('10, 0, 10, 20: LineXYtoWH=', x, y, w, h, ' / angle & lenth=', a, l)
562
x, y, w, h= geohelp.LineXYtoWH(10, 20, 10, 0)
563
a, l= geohelp.GetPol(w, h)
564
print('10, 20, 10, 0: LineXYtoWH=', x, y, w, h, ' / angle & lenth=', a, l)
567
print('Test IntersSegm')
568
print('---------------')
569
testlist= [(10, 20, 40, 20), (-100, -10, -10, 30), (-40, 20, -40, -60), (5, -5, 20, -70)]
570
print('should be (15.0, 20.0, False), (15.0, approx 41, False), (15.0, approx -48, True)')
571
print('it is', geohelp.IntersSegm((15, -60, 15, -5), testlist))
572
print('should be (approx -21, 25.0, True), (-40.0, 25.0, False), (approx -2, 25.0, False)')
573
print('it is', geohelp.IntersSegm((-35, 25, -5, 25), testlist))
574
print('should be (approx 214, 20.0, False), (approx -149, approx -32, False), (-40.0, approx -16, True), (approx 6, approx -10, True')
575
print('it is', geohelp.IntersSegm((11, -9, -59, -19), testlist))
577
print('should be (approx -3, 20.0, True), (approx -13, approx 28, True), (-40.0, approx 49, False), (approx 0, approx 18, True')
578
print('it is', geohelp.IntersSegm((-18, 32, 11, 9), testlist))