def initPoint(P, dir, offset): P = Vector(P[0], P[1]) if dir == 'X+': P[0] += offset elif dir == 'X-': P[0] -= offset elif dir == 'Y+': P[1] += offset elif dir == 'Y-': P[1] -= offset return P
def convert(self, value, units): """Convert units to another format""" f = self._TOMM[self.units] / DXF._TOMM[units] if isinstance(value,float): return value * f elif isinstance(value,Vector): new = Vector(value) for i in range(len(value)): new[i] *= f return new elif isinstance(value,list): new = [] for x in value: new.append(x*f) return new else: raise Exception("Cannot convert type %s %s"%(type(value),str(value)))
def _calcEndPoints(self): if self.type == "LINE": self._start = self.point() self._end = self.point(1) elif self.type == "CIRCLE": x,y = self.point() r = self.radius() self._start = self._end = Vector(x+r,y) elif self.type == "ARC": x,y = self.point() r = self.radius() s = math.radians(self.startPhi()) self._start = Vector(x+r*math.cos(s), y + r*math.sin(s)) s = math.radians(self.endPhi()) self._end = Vector(x+r*math.cos(s), y + r*math.sin(s)) elif self.type in ("POLYLINE", "LWPOLYLINE", "SPLINE"): self._start = Vector(self[10][0], self[20][0]) if self.isClosed(): self._end = Vector(self[10][0], self[20][0]) else: self._end = Vector(self[10][-1], self[20][-1]) elif self.type in ("POINT", "ELLIPSE", "DIMENSION", "@START"): self._start = self._end = self.point() else: error("Cannot handle entity type: %s in layer: %s\n"%(self.type, self.name)) #import traceback; traceback.print_stack() self._start = self._end = self.point()
def _intersectLineArc(self, arc): #AB = self.B #a = AB.length2() a = self.AB[0]**2 + self.AB[1]**2 if a < EPS2: return None, None #CA = self.start-arc.center #b = 2.0*AB*CA #c = CA.length2() - arc.radius**2 CAx = self.start[0] - arc.center[0] CAy = self.start[1] - arc.center[1] b = 2.0 * (self.AB[0] * CAx + self.AB[1] * CAy) c = CAx**2 + CAy**2 - arc.radius**2 t1, t2 = quadratic(b / a, c / a) if t1 is None: return None, None if t1 < -EPS or t1 > 1.0 + EPS: P1 = None elif t1 <= EPS: P1 = self.start elif t1 >= 1.0 - EPS: P1 = self.end else: #P1 = AB*t1 + self.start P1 = Vector(self.AB[0] * t1 + self.start[0], self.AB[1] * t1 + self.start[1]) if P1 and not arc._insideArc(P1): P1 = None if t2 < -EPS or t2 > 1.0 + EPS: P2 = None elif t2 <= EPS: P2 = self.start elif t2 >= 1.0 - EPS: P2 = self.end else: #P2 = AB*t2 + self.start P2 = Vector(self.AB[0] * t2 + self.start[0], self.AB[1] * t2 + self.start[1]) if P2 and not arc._insideArc(P2): P2 = None return P1, P2
def start(self): if self._start is not None: return self._start if self.type == "LINE": self._start = self.point() elif self.type == "CIRCLE": x, y = self.point() r = self.radius() self._start = self._end = Vector(x + r, y) elif self.type == "ARC": x, y = self.point() r = self.radius() s = math.radians(self.startPhi()) self._start = Vector(x + r * math.cos(s), y + r * math.sin(s)) elif self.type == "LWPOLYLINE": self._start = Vector(self[10][0], self[20][0]) #elif self.type == "ELLIPSE": #elif self.type == "SPLINE": else: raise Exception("Cannot handle entity type %s" % (self.type)) return self._start
def start(self): if self._start is not None: return self._start elif self.type == "LINE": self._start = self.point() elif self.type == "CIRCLE": x, y = self.point() r = self.radius() self._start = self._end = Vector(x + r, y) elif self.type == "ARC": x, y = self.point() r = self.radius() s = math.radians(self.startPhi()) self._start = Vector(x + r * math.cos(s), y + r * math.sin(s)) elif self.type in ("POLYLINE", "LWPOLYLINE", "SPLINE"): self._start = Vector(self[10][0], self[20][0]) elif self.type in ("POINT", "ELLIPSE"): self._start = self.point() else: #raise Exception("Cannot handle entity type %s"%(self.type)) error("Cannot handle entity type: %s in layer: %s\n" % (self.type, self.name)) self._start = self.point() return self._start
def end(self): if self._end is not None: return self._end if self.type == "LINE": self._end = self.point(1) elif self.type == "CIRCLE": x,y = self.point() r = self.radius() self._start = self._end = Vector(x+r,y) elif self.type == "ARC": x,y = self.point() r = self.radius() s = math.radians(self.endPhi()) self._end = Vector(x+r*math.cos(s), y + r*math.sin(s)) elif self.type == "LWPOLYLINE": self._end = Vector(self[10][-1], self[20][-1]) elif self.type == "POINT": self._end = self.point() else: #raise Exception("Cannot handle entity type %s"%(self.type)) sys.stderr.write("Cannot handle entity type %s: %s\n"%(self.type, self.name)) self._end = self.point() return self._end
def circle3center(A,B,C): try: xDelta_a = B[0] - A[0] yDelta_a = B[1] - A[1] xDelta_b = C[0] - B[0] yDelta_b = C[1] - B[1] center = Vector(0, 0) aSlope = yDelta_a/xDelta_a bSlope = yDelta_b/xDelta_b center[0] = (aSlope*bSlope*(A[1] - C[1]) + bSlope*(A[0] + B[0]) - aSlope*(B[0]+C[0]) )/(2* (bSlope-aSlope) ) center[1] = -1*(center[0] - (A[0]+B[0])/2)/aSlope + (A[1]+B[1])/2 return center except: return None
def extrapolatePoint(self, dist, B=False): if self.type == Segment.LINE: if not B: return self.A+(self.tangentStart()*dist) else: return self.B+(self.tangentStart()*dist) else: if self.type == Segment.CW: dist = -dist raddist = dist/self.radius if not B: phi = self.startPhi+raddist else: phi = self.endPhi+raddist return Vector( self.C[0] + self.radius*cos(phi), self.C[1] + self.radius*sin(phi))
def isInside(self, P): #print "P=",P #minx,miny,maxx,maxy = self.bbox() maxx = self.bbox()[2] #print "limits:",minx,miny,maxx,maxy #FIXME: this is strange. adding +1000 to line endpoint changes the outcome of method # i've found that doing this works around some unknown problem in most cases, but it's not really ideal solution line = Segment(Segment.LINE, P, Vector(maxx*1.1, P[1]+1000)) count = 0 PP1 = None # previous points to avoid double counting PP2 = None #print "Line=",line for segment in self: P1,P2 = line.intersect(segment) #print #print i,segment if P1 is not None: if PP1 is None and PP2 is None: count += 1 elif PP1 is not None and PP2 is not None and \ not eq(P1,PP1) and not eq(P1,PP2): count += 1 elif PP1 is not None and not eq(P1,PP1): count += 1 elif PP2 is not None and not eq(P1,PP2): count += 1 if P2 is not None: if eq(P1,P2): P2 = None elif PP1 is None and PP2 is None: count += 1 elif PP1 is not None and PP2 is not None and \ not eq(P2,PP1) and not eq(P2,PP2): count += 1 elif PP1 is not None and not eq(P2,PP1): count += 1 elif PP2 is not None and not eq(P2,PP2): count += 1 #print P1,P2,count PP1 = P1 PP2 = P2 #print "Count=",count return bool(count&1)
def isInside(self, P): #print "P=",P #minx,miny,maxx,maxy = self.bbox() maxx = self.bbox()[2] #print "limits:",minx,miny,maxx,maxy line = Segment(Segment.LINE, P, Vector(maxx * 1.1, P[1])) count = 0 PP1 = None # previous points to avoid double counting PP2 = None #print "Line=",line for segment in self: P1, P2 = line.intersect(segment) #print #print i,segment if P1 is not None: if PP1 is None and PP2 is None: count += 1 elif PP1 is not None and PP2 is not None and \ not eq(P1,PP1) and not eq(P1,PP2): count += 1 elif PP1 is not None and not eq(P1, PP1): count += 1 elif PP2 is not None and not eq(P1, PP2): count += 1 if P2 is not None: if eq(P1, P2): P2 = None elif PP1 is None and PP2 is None: count += 1 elif PP1 is not None and PP2 is not None and \ not eq(P2,PP1) and not eq(P2,PP2): count += 1 elif PP1 is not None and not eq(P2, PP1): count += 1 elif PP2 is not None and not eq(P2, PP2): count += 1 #print P1,P2,count PP1 = P1 PP2 = P2 #print "Count=",count return bool(count & 1)
def _intersectLineArc(self, arc): #AB = self.B #a = AB.length2() a = self.AB[0]**2 + self.AB[1]**2 if a < EPS2: return None, None #CA = self.start-arc.center #b = 2.0*AB*CA #c = CA.length2() - arc.radius**2 CAx = self.start[0] - arc.center[0] CAy = self.start[1] - arc.center[1] b = 2.0 * (self.AB[0] * CAx + self.AB[1] * CAy) #c = CAx**2 + CAy**2 - arc.radius**2 if abs(CAx) < abs(CAy): c = CAy**2 + (CAx + arc.radius) * (CAx - arc.radius) else: c = CAx**2 + (CAy + arc.radius) * (CAy - arc.radius) t1, t2 = quadratic(b / a, c / a) if t1 is None: return None, None if t1 < -EPS or t1 > 1.0 + EPS: P1 = None elif t1 <= EPS: P1 = Vector(self.start) elif t1 >= 1.0 - EPS: P1 = Vector(self.end) else: #P1 = AB*t1 + self.start P1 = Vector(self.AB[0] * t1 + self.start[0], self.AB[1] * t1 + self.start[1]) if P1 and not arc._insideArc(P1): P1 = None if t2 < -EPS or t2 > 1.0 + EPS: P2 = None elif t2 <= EPS: P2 = Vector(self.start) elif t2 >= 1.0 - EPS: P2 = Vector(self.end) else: #P2 = AB*t2 + self.start P2 = Vector(self.AB[0] * t2 + self.start[0], self.AB[1] * t2 + self.start[1]) if P2 and not arc._insideArc(P2): P2 = None # force P1 to have always the solution if any if P1 is None: return P2, None return P1, P2
def fromDxf(self, dxf, layer, units=0): for entity in layer: self.color = entity.color() start = dxf.convert(entity.start(), units) end = dxf.convert(entity.end(), units) if entity.type == "LINE": if not eq(start,end): self.append(Segment(Segment.LINE, start, end)) elif entity.type == "CIRCLE": center = dxf.convert(entity.center(), units) self.append(Segment(Segment.CCW, start, end, center)) elif entity.type == "ARC": t = entity._invert and Segment.CW or Segment.CCW center = dxf.convert(entity.center(), units) self.append(Segment(t, start, end, center)) elif entity.type in ("POLYLINE", "LWPOLYLINE", "SPLINE"): # split it into multiple line segments xy = list(zip(dxf.convert(entity[10],units), dxf.convert(entity[20],units))) if entity.isClosed(): xy.append(xy[0]) bulge = entity.bulge() if not isinstance(bulge,list): bulge = [bulge]*len(xy) if entity._invert: # reverse and negate bulge xy.reverse() bulge = [-x for x in bulge[::-1]] for i,(x,y) in enumerate(xy[1:]): b = bulge[i] end = Vector(x,y) if eq(start,end): continue if abs(b)<EPS: self.append(Segment(Segment.LINE, start, end)) elif abs(b-1.0)<EPS: # Semicircle center = (start+end)/2.0 if b<0.0: t = Segment.CW else: t = Segment.CCW self.append(Segment(t, start, end, center)) else: # arc with bulge = b # b = tan(theta/4) theta = 4.0*atan(abs(b)) if abs(b)>1.0: theta = 2.0*pi - theta AB = start-end ABlen = AB.length() d = ABlen / 2.0 r = d / sin(theta/2.0) C = (start+end)/2.0 try: OC = sqrt((r-d)*(r+d)) if b<0.0: t = Segment.CW else: t = Segment.CCW OC = -OC if abs(b)>1.0: OC = -OC center = Vector(C[0] - OC*AB[1]/ABlen, C[1] + OC*AB[0]/ABlen) self.append(Segment(t, start, end, center)) except: self.append(Segment(Segment.LINE, start, end)) start = end
def point3D(self, idx=0): """return 3D point 10+idx,20+idx""" return Vector(self.get(10 + idx), self.get(20 + idx), self.get(30 + idx))
def point(self, idx=0): """return 2D point 10+idx,20+idx""" return Vector(self.get(10 + idx, 0), self.get(20 + idx, 0))
def point(self, idx=0): return Vector(self.get(10 + idx, 0), self.get(20 + idx, 0))
def point_on_circle(self, radius, angle): return Vector(radius * math.cos(angle), radius * math.sin(angle))
def point3D(self, idx=0): return Vector(self.get(10 + idx), self.get(20 + idx), self.get(30 + idx))