def byCenter2Points(cls, pc, p1, p2): """Create an ellipse by center and 2 points for axis. /----p2----\ / \ | pc p1 \ / \----------/ """ if not isinstance(pc, (QgsPoint, CADPoint)) \ or not isinstance(p1, (QgsPoint, CADPoint)) \ or not isinstance(p2, (QgsPoint, CADPoint)): raise AttributeError pc = CADPoint(pc) p1 = CADPoint(p1) p2 = CADPoint(p2) angle_exist = pc.getAngleOfLineBetweenTwoPoints(p1) axis_a = pc.distance(p1) pp = CADLine.pointOnALine(CADLine(pc, p1), p2) length = p2.distance(pp) pp2 = pc.pointProjected(length, 90 + angle_exist) axis_b = pc.distance(pp2) return cls(pc, axis_a, axis_b, angle_exist)
def by2Corners(cls, p1, p2, nbEdges=5): """CADRegularPolygon by 2 corners (AB): A----B / \ / \ \ / \------/ """ if not isinstance(p1, (QgsPoint, CADPoint)) or \ not isinstance(p2, (QgsPoint, CADPoint)) or \ not isinstance(nbEdges, (long, int)): raise AttributeError else: p1 = CADPoint(p1) p2 = CADPoint(p2) angle_exist = p1.getAngleOfLineBetweenTwoPoints(p2) pm = p1.midpoint(p2) length = p1.distance(pm) angle = (180.0 - (360.0 / nbEdges)) / 2.0 hypo = length / math.cos(math.radians(angle)) pc = p1.pointProjected(hypo, angle_exist + angle) return cls(pc, p1, nbEdges) return None
def by3Points(cls, p1, p2, p3): """Create a CADRectangle with 2 points (AD) and a length (X - AD) D-------C | X A-------B """ if not isinstance(p1, (CADPoint, QgsPoint)) or \ not isinstance(p2, (CADPoint, QgsPoint)) or \ not isinstance(p3, (CADPoint, QgsPoint)): raise AttributeError p1 = CADPoint(p1) p2 = CADPoint(p2) p3 = CADPoint(p3) l = CADLine(p1, p2) angle_exist = p1.getAngleOfLineBetweenTwoPoints(p2) side = CADPoint.isCollinear(p1, p2, p3) if side == 0: return None pp = CADLine.pointOnALine(CADLine(p1, p2), p3) length = p3.distance(pp) * side p3 = p2.pointProjected(length, 90 + angle_exist) p4 = p1.pointProjected(length, 90 + angle_exist) return cls(p1, p2, p3, p4)
def perpendicular_segment(self, p): """Create a perpendicular line segment from `p` to this line. The enpoints of the segment are ``p`` and the closest point in the line containing self. (If self is not a line, the point might not be in self.) """ if not isinstance(p, (QgsPoint, CADPoint)): raise AttributeError p = CADPoint(p) if p in self.points: return p a, b, c = self.coefficients if a == 0: # horizontal p2 = CADPoint(p.x, self.p1.y) elif b == 0: # vertical p2 = CADPoint(self.p1.x, p.y) else: # ax + by + c = 0 y = (-c - a * p.x) / b m = self.slope d2 = 1 + m**2 H = p.y - y dx = m * H / d2 dy = m * dx p2 = CADPoint(p.x + dx, y + dy) return CADLine(p, p2)
def by2PointsAndWidth(cls, p1, p2, w): """Create a CADRectangle with 2 points (AD) and the width D-------C | | A-------B """ if not isinstance(p1, (CADPoint, QgsPoint)) or \ not isinstance(p2, (CADPoint, QgsPoint)) or \ not isinstance(w, (float, long, int)): raise AttributeError p1 = CADPoint(p1) p2 = CADPoint(p2) angle_exist = p1.getAngleOfLineBetweenTwoPoints(p2) pt1 = p1 pt2 = p1.pointProjected(w, 90 - angle_exist) pt3 = p2.pointProjected(w, 90 - angle_exist) pt4 = p2 l = CADLine(p1, p2) if CADPoint.isCollinear(pt1, pt2, pt3) == 0: pt2 = p1.pointProjected(w, 90 + angle_exist) pt3 = p2.pointProjected(w, 90 + angle_exist) return cls(pt1, pt2, pt3, pt4)
def fromFoci(cls, f1, f2, f3): """Create an ellipse from foci. f1 and f2 are foci f3 is the axis_b /----f3----\ / \ | f1 f2 | \ / \----------/ """ if not isinstance(f1, (QgsPoint, CADPoint)) \ or not isinstance(f2, (QgsPoint, CADPoint)) \ or not isinstance(f3, (QgsPoint, CADPoint)): raise AttributeError f1 = CADPoint(f1) f2 = CADPoint(f2) f3 = CADPoint(f3) dist_f1f2 = f1.distance(f2) dist_f1f3 = f1.distance(f3) dist_f2f3 = f2.distance(f3) dist_tot = dist_f1f3 + dist_f2f3 angle_exist = f1.getAngleOfLineBetweenTwoPoints(f2) center_f1f2 = f1.midpoint(f2) axis_a = dist_tot / 2.0 axis_b = math.sqrt((dist_tot / 2.0)**2.0 - (dist_f1f2 / 2.0)**2.0) return cls(center_f1f2, axis_a, axis_b, angle_exist)
def quadrant(self): """Return 4 coordinates [East, North, West, South] of circle's quadrant""" N = CADPoint(self._center.x, self._center.y + self._radius) S = CADPoint(self._center.x, self._center.y - self._radius) E = CADPoint(self._center.x + self._radius, self._center.y) W = CADPoint(self._center.x - self._radius, self._center.y) return [E, N, W, S]
def fromQgsRectangle(cls, r): """Create a CADRectangle from a QgsRectangle """ if not isinstance(r, QgsRectangle): raise AttributeError("Error:" + cls.__name__ + " - " + __file__) return cls() return cls.byExtent(CADPoint(r.xMinimum(), r.yMinimum()), CADPoint(r.xMaximum(), r.yMaximum()))
def by3Points(cls, p1, p2, p3, epsilon=1e-8): """Create a CADCircle with 3 points ABC /------\ | | A C | | \--B---/ """ if not all(isinstance(p, (QgsPoint, CADPoint)) for p in [p1, p2, p3]): raise AttributeError p1 = CADPoint(p1) p2 = CADPoint(p2) p3 = CADPoint(p3) # Paul Bourke's algorithm m_Center = CADPoint() m_dRadius = -1 yDelta_a = p2.y - p1.y xDelta_a = p2.x - p1.x yDelta_b = p3.y - p2.y xDelta_b = p3.x - p2.x try: aSlope = yDelta_a / xDelta_a except ZeroDivisionError: return None try: bSlope = yDelta_b / xDelta_b except ZeroDivisionError: return None if (math.fabs(xDelta_a) <= epsilon and math.fabs(yDelta_b) <= epsilon): m_Center.x = (0.5 * (p2.x + p3.x)) m_Center.y = (0.5 * (p1.y + p2.y)) m_dRadius = m_Center.distance(p1) return cls(m_Center, m_dRadius) if math.fabs(aSlope-bSlope) <= epsilon: return None m_Center.x = ( (aSlope * bSlope * (p1.y - p3.y) + bSlope * (p1.x + p2.x) - aSlope * (p2.x + p3.x)) / (2.0 * (bSlope - aSlope)) ) m_Center.y = ( -1.0 * (m_Center.x - (p1.x + p2.x) / 2.0) / aSlope + (p1.y + p2.y) / 2.0 ) m_dRadius = m_Center.distance(p1) return cls(m_Center, m_dRadius)
def __init__(self, p1, p2): if not all([isinstance(p, (QgsPoint, CADPoint)) for p in [p1, p2]]): raise AttributeError if p1 == p2: # Adapted from SymPy # if it makes sense to return a Point, handle in subclass raise ValueError("CADLine requires two unique Points.") self._p1 = CADPoint(p1) self._p2 = CADPoint(p2)
def __init__(self, pCenter, pCorner, nbEdges=5): if not isinstance(pCenter, (QgsPoint, CADPoint)) \ or not isinstance(pCorner, (QgsPoint, CADPoint)) \ or not isinstance(nbEdges, (long, int)): raise AttributeError else: self._center = CADPoint(pCenter) self._corner = CADPoint(pCorner) self._nbEdges = nbEdges self.__points = self.__updatePoints()
def __init__(self, ptStart, ptEnd, ptArc): if not all([ isinstance(p, (CADPoint, QgsPoint)) for p in [ptStart, ptEnd, ptArc] ]): raise AttributeError else: self._ptStart = CADPoint(ptStart) self._ptEnd = CADPoint(ptEnd) self._ptArc = CADPoint(ptArc) self._ptCenter = self.__getArcCenter()
def byCenterPoint(cls, pc, p1): """Create a CADCircle by Extent CA /------\A | | | C | | | \------/ """ if not all(isinstance(p, (QgsPoint, CADPoint)) for p in [p1, pc]): raise AttributeError p1 = CADPoint(p1) pc = CADPoint(pc) return cls(pc, pc.distance(p1))
def trim(self, other, p): """Return the new CADLine result of intersection with other. For segment AB intersected at i and clicked on p, the CADLine returned will iB A-----i------p---B return: i----------B """ if isinstance(p, (QgsPoint, CADPoint)) and \ isinstance(other, CADLine) and \ self.intersects(other): p = CADPoint(p) try: pt_inter = self.intersection(other) p1pi = CADLine(pt_inter, self.p1) p2pi = CADLine(self.p2, pt_inter) if p1pi.isOnSegment(p) == 2: return p1pi elif p2pi.isOnSegment(p) == 2: return p2pi else: return None except: return None return None else: raise AttributeError
def __init__(self, pc, radius): if not isinstance(pc, (CADPoint, QgsPoint)) or \ not isinstance(radius, (float, long, int)): raise AttributeError else: self._center = CADPoint(pc) self._radius = radius
def isOnSegment(self, p): """Test if this point is on the segment defined by points a, b. Returns 0 if this point is not on the open ray through a and b, 1 if point is on open ray a, 2 if point is within line segment, 3 if point is on open ray b. """ if not isinstance(p, (QgsPoint, CADPoint)): raise AttributeError p = CADPoint(p) a, b = self.points # algorithm from 'graphics GEMS', # A. Paeth: 'A Fast 2D Point-on-line test' if (abs((b.y - a.y) * (p.x - a.x) - (p.y - a.y) * (b.x - a.x)) >= max(abs(b.x - a.x), abs(b.y - a.y))): return 0 if ((b.x < a.x and a.x < p.x) or (b.y < a.y and a.y < p.y)): return 1 if ((p.x < a.x and a.x < b.x) or (p.y < a.y and a.y < b.y)): return 1 if ((a.x < b.x and b.x < p.x) or (a.y < b.y and b.y < p.y)): return 3 if ((p.x < b.x and b.x < a.x) or (p.y < b.y and b.y < a.y)): return 3 return 2
def corner(self, pc): """Set the first corner (edge) of the RegularPolygon """ if not isinstance(pc, (QgsPoint, CADPoint)): raise AttributeError self._corner = CADPoint(pc) self.__update()
def isValid(self, tolerance=0.1): """Return True if is really a rectangle""" if not isinstance(tolerance, (float, int, long)): tolerance = 0.1 p1, p2, p3, p4 = self.__points[:] p12 = p1.getAngleOfLineBetweenTwoPoints(p2) p23 = p2.getAngleOfLineBetweenTwoPoints(p3) a1 = p23 - p12 p34 = p3.getAngleOfLineBetweenTwoPoints(p4) p41 = p4.getAngleOfLineBetweenTwoPoints(p1) a2 = p41 - p34 if self.__points == [CADPoint(0.0, 0.0)] * 4: return False if len(set(self.__points)) != 4: return False if not near(a1 % 90, 0.0, tolerance) or not near( a2 % 90, 0.0, tolerance): return False return True
def __contains__(self, other): if isinstance(other, (QgsPoint, CADPoint)): other = CADPoint(other) return bool(self.isOnSegment(other) == 2) elif isinstance(other, CADLine): return self.intersects(other) else: return False
def byCenter2Points(cls, ptCenter, ptStart, ptEnd, direction=1): """Create a CADCirclurArc with by Center C and 2 points AB A C B | | \------/ """ if all([isinstance(p, (CADPoint, QgsPoint)) for p in [ptCenter, ptStart, ptEnd]]) and \ isinstance(direction, (int, long)): ptCenter = CADPoint(ptCenter) ptStart = CADPoint(ptStart) ptEnd = CADPoint(ptEnd) a1 = ptCenter.getAngleOfLineBetweenTwoPoints(ptStart) a2 = ptCenter.getAngleOfLineBetweenTwoPoints(ptEnd) return cls.byCenterPointAngle(ptCenter, ptStart, a2 - a1, direction)
def __iter__(self, segments=36): angle = math.radians(self._angle) for t in [(2 * math.pi) / segments * i for i in range(segments)]: p = CADPoint( self._center.x + self._axis_a * math.cos(t) * math.cos(angle) - self._axis_b * math.sin(t) * math.sin(angle), self._center.y + self._axis_a * math.cos(t) * math.sin(angle) + self._axis_b * math.sin(t) * math.cos(angle)) yield p
def by2Points(cls, p1, p2): """Create a CADCircle with 2 points (from diameter : AB) /------\ | | A B | | \------/ """ if not all(isinstance(p, (QgsPoint, CADPoint)) for p in [p1, p2]): raise AttributeError p1 = CADPoint(p1) p2 = CADPoint(p2) center = p1.midpoint(p2) radius = p1.distance(center) return cls(center, radius)
def nearestPoints(self, other): if isinstance(other, (QgsPoint, CADPoint)): other = CADPoint(other) dist = [other.distance(p) for p in self.points] neareastDist = min(dist) idx = dist.index(neareastDist) return (self.points[idx], idx) else: raise AttributeError
def center(self, pointC): if isinstance(pointC, CADPoint): offsetX = pointC.x - self._center.x offsetY = pointC.y - self._center.y self._center = pointC for i, p in enumerate(self.__points): self.__points[i] = CADPoint(p.x + offsetX, p.y + offsetY) self.__updateMin() self.__updateMax
def perpendicular_line(self, p): """Create a new Line perpendicular to this linear entity which passes through the point `p`. """ if not isinstance(p, (QgsPoint, CADPoint)): raise AttributeError p = CADPoint(p) d1, d2 = (self.p1.x - self.p2.x, self.p1.y - self.p2.y) if d2 == 0: # If a horizontal line if p.y == self.p1.y: # if p is on this linear entity return CADLine(p, p + CADPoint(0, 1)) else: p2 = CADPoint(p.x, self.p1.y) return CADLine(p, p2) else: p2 = CADPoint(p.x - d2, p.y + d1) return CADLine(p, p2)
def parallel_line(self, p): """Create a new Line parallel to this linear entity which passes through the point `p`. """ if not isinstance(p, (QgsPoint, CADPoint)): raise AttributeError p = CADPoint(p) d = self.p1 - self.p2 return CADLine(p, p + d)
def pointOnALine(line, pp): """Return orthogonal point projected (pp) on line """ if not isinstance(pp, (QgsPoint, CADPoint)) or \ not isinstance(line, CADLine): raise AttributeError p1, p2 = line.points pp = CADPoint(pp) # http://stackoverflow.com/a/15187473 t_num = ((pp.x - p1.x) * (p2.x - p1.x) + (pp.y - p1.y) * (p2.y - p1.y)) t_denum = ((p2.x - p1.x)**2 + (p2.y - p1.y)**2) t = t_num / t_denum Dx = p1.x + t * (p2.x - p1.x) Dy = p1.y + t * (p2.y - p1.y) return CADPoint(Dx, Dy)
def by2Points(cls, p1, p2): """Create a CADRectangle with 2 points (from extent : AC) D-------C | | A-------B """ if not isinstance(p1, (CADPoint, QgsPoint)) or \ not isinstance(p2, (CADPoint, QgsPoint)): raise AttributeError p1 = CADPoint(p1) p2 = CADPoint(p2) pt0 = p1 pt1 = CADPoint(p2.x, p1.y) pt2 = p2 pt3 = CADPoint(p1.x, p2.y) return cls(pt0, pt1, pt2, pt3)
def center(self, pc): """Set the center of the RegularPolygon """ if not isinstance(pc, (QgsPoint, CADPoint)): raise AttributeError angle_exist = self._rotation dist_exist = self._radius self._center = CADPoint(pc) self._corner = self._center.pointProjected(dist_exist, angle_exist) self._update()
def byCenterPoint(cls, pc, p1): """Create an ellipse by this center and an extent point /----------\ p1 / \ | pc | \ / \----------/ """ if not isinstance(pc, (QgsPoint, CADPoint)) \ or not isinstance(p1, (QgsPoint, CADPoint)): raise AttributeError pc = CADPoint(pc) p1 = CADPoint(p1) xOffset = abs(pc.x - p1.x) yOffset = abs(pc.y - p1.y) return cls(pc, xOffset, yOffset)