| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- #!/usr/bin/env python
- #
- # This code is part of Grandalf
- # Copyright (C) 2008 Axel Tillequin (bdcht3@gmail.com) and others
- # published under GPLv2 license or EPLv1 license
- # Contributor(s): Axel Tillequin, Fabio Zadrozny
- from .poset import *
- from .dot import *
- from math import atan2, sqrt
- from random import SystemRandom
- # ------------------------------------------------------------------------------
- def intersect2lines(xy1, xy2, xy3, xy4):
- (x1, y1) = xy1
- (x2, y2) = xy2
- (x3, y3) = xy3
- (x4, y4) = xy4
- b = (x2 - x1, y2 - y1)
- d = (x4 - x3, y4 - y3)
- det = b[0] * d[1] - b[1] * d[0]
- if det == 0:
- return None
- c = (x3 - x1, y3 - y1)
- t = float(c[0] * b[1] - c[1] * b[0]) / (det * 1.0)
- if t < 0.0 or t > 1.0:
- return None
- t = float(c[0] * d[1] - c[1] * d[0]) / (det * 1.0)
- if t < 0.0 or t > 1.0:
- return None
- x = x1 + t * b[0]
- y = y1 + t * b[1]
- return (x, y)
- # ------------------------------------------------------------------------------
- # intersectR returns the intersection point between the Rectangle
- # (w,h) that characterize the view object and the line that goes
- # from the views' object center to the 'topt' point.
- def intersectR(view, topt):
- # we compute intersection in local views' coord:
- # center of view is obviously :
- x1, y1 = 0, 0
- # endpoint in view's coord:
- x2, y2 = topt[0] - view.xy[0], topt[1] - view.xy[1]
- # bounding box:
- bbx2 = view.w // 2
- bbx1 = -bbx2
- bby2 = view.h // 2
- bby1 = -bby2
- # all 4 segments of the bb:
- S = [
- ((x1, y1), (x2, y2), (bbx1, bby1), (bbx2, bby1)),
- ((x1, y1), (x2, y2), (bbx2, bby1), (bbx2, bby2)),
- ((x1, y1), (x2, y2), (bbx1, bby2), (bbx2, bby2)),
- ((x1, y1), (x2, y2), (bbx1, bby2), (bbx1, bby1)),
- ]
- # check intersection with each seg:
- for segs in S:
- xy = intersect2lines(*segs)
- if xy != None:
- x, y = xy
- # return global coord:
- x += view.xy[0]
- y += view.xy[1]
- return (x, y)
- # there can't be no intersection unless the endpoint was
- # inside the bb !
- raise ValueError(
- "no intersection found (point inside ?!). view: %s topt: %s" % (view, topt)
- )
- # ------------------------------------------------------------------------------
- def getangle(p1, p2):
- x1, y1 = p1
- x2, y2 = p2
- theta = atan2(y2 - y1, x2 - x1)
- return theta
- # ------------------------------------------------------------------------------
- def median_wh(views):
- mw = [v.w for v in views]
- mh = [v.h for v in views]
- mw.sort()
- mh.sort()
- return (mw[len(mw) // 2], mh[len(mh) // 2])
- # ------------------------------------------------------------------------------
- try:
- from numpy import array, matrix, cos, sin
- has_numpy = True
- except ImportError:
- has_numpy = False
- from math import cos, sin, pi
- from .linalg import array, matrix
- # rand_ortho1 returns a numpy.array representing
- # a random normalized n-dimension vector orthogonal to (1,1,1,...,1).
- def rand_ortho1(n):
- r = SystemRandom()
- pos = [r.random() for x in range(n)]
- s = sum(pos)
- v = array(pos, dtype=float) - (s / float(n))
- norm = sqrt(sum(v * v))
- return v / norm
- # ------------------------------------------------------------------------------
- # intersectC returns the intersection point between the Circle
- # of radius r and centered on views' position with the line
- # to the 'topt' point.
- def intersectC(view, r, topt):
- theta = getangle(view.xy, topt)
- x = int(cos(theta) * r)
- y = int(sin(theta) * r)
- return (x, y)
- # ------------------------------------------------------------------------------
- # setcurve returns the spline curve that path through the list of points P.
- # The spline curve is a list of cubic bezier curves (nurbs) that have
- # matching tangents at their extreme points.
- # The method considered here is taken from "The NURBS book" (Les A. Piegl,
- # Wayne Tiller, Springer, 1997) and implements a local interpolation rather
- # than a global interpolation.
- def setcurve(e, pts, tgs=None):
- P = list(map(array, pts))
- n = len(P)
- # tangent estimation
- if tgs:
- assert len(tgs) == n
- T = list(map(array, tgs))
- Q = [P[k + 1] - P[k] for k in range(0, n - 1)]
- else:
- Q, T = tangents(P, n)
- splines = []
- for k in range(n - 1):
- t = T[k] + T[k + 1]
- a = 16.0 - (t.dot(t))
- b = 12.0 * (Q[k].dot(t))
- c = -36.0 * Q[k].dot(Q[k])
- D = (b * b) - 4.0 * a * c
- assert D >= 0
- sd = sqrt(D)
- s1, s2 = (-b - sd) / (2.0 * a), (-b + sd) / (2.0 * a)
- s = s2
- if s1 >= 0:
- s = s1
- C0 = tuple(P[k])
- C1 = tuple(P[k] + (s / 3.0) * T[k])
- C2 = tuple(P[k + 1] - (s / 3.0) * T[k + 1])
- C3 = tuple(P[k + 1])
- splines.append([C0, C1, C2, C3])
- return splines
- # ------------------------------------------------------------------------------
- def tangents(P, n):
- assert n >= 2
- Q = []
- T = []
- for k in range(0, n - 1):
- q = P[k + 1] - P[k]
- t = q / sqrt(q.dot(q))
- Q.append(q)
- T.append(t)
- T.append(t)
- return (Q, T)
- # ------------------------------------------------------------------------------
- def setroundcorner(e, pts):
- P = list(map(array, pts))
- n = len(P)
- Q, T = tangents(P, n)
- c0 = P[0]
- t0 = T[0]
- k0 = 0
- splines = []
- k = 1
- while k < n:
- z = abs(t0[0] * T[k][1] - (t0[1] * T[k][0]))
- if z < 1.0e-6:
- k += 1
- continue
- if (k - 1) > k0:
- splines.append([c0, P[k - 1]])
- if (k + 1) < n:
- splines.extend(setcurve(e, [P[k - 1], P[k + 1]], tgs=[T[k - 1], T[k + 1]]))
- else:
- splines.extend(setcurve(e, [P[k - 1], P[k]], tgs=[T[k - 1], T[k]]))
- break
- if (k + 2) < n:
- c0 = P[k + 1]
- t0 = T[k + 1]
- k0 = k + 1
- k += 2
- else:
- break
- return splines or [[P[0], P[-1]]]
- # ------------------------------------------------------------------------------
- def new_point_at_distance(pt, distance, angle):
- # angle in radians
- distance = float(distance)
- x, y = pt[0], pt[1]
- x += distance * cos(angle)
- y += distance * sin(angle)
- return x, y
|