##############################################################################
# ThanCad 0.0.9 "DoesSomething": 2dimensional CAD with raster support for engineers.
#
# Copyright (c) 2001-2009 Thanasis Stamos, August 23, 2009
# URL: http://thancad.sourceforge.net
# e-mail: cyberthanasis@excite.com
#
# This program 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 2 of the License, or
# (at your option) any later version.
#
# This program 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 (www.gnu.org/licenses/gpl.html).
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
"""\
ThanCad 0.0.9 "DoesSomething": 2dimensional CAD with raster support for engineers.
This module defines the dimension element.
"""
from math import hypot,atan2,fabs
from p_gmath import thanNear2
from itertools import izip,islice
from thanelem import ThanElement
from thanline import ThanLine
from thantext import ThanText
from thanvar import Canc
from thantrans import T
try: import pyx
except ImportError: pass
class ThanDimali(ThanElement):
"A simple line of horizontal text."
thanTkCompound = 100 # The number of Tkinter objects that make the element. 100=compound (lines etc.)
def __rect(self, c1, c2, h):
"Finds rotated rectangle with c1-c2 side and parallel side at perpendicular distance h."
c3 = list(c2)
c4 = list(c1)
if thanNear2(c1, c2):
cost = 0.0
sint = 1.0
else:
cost = c2[0]-c1[0]
sint = c2[1]-c1[1]
w = hypot(cost, sint)
cost /= w
sint /= w
# xb = xa + w*cost
# yb = ya + w*sint
c3[0] = c2[0] - h*sint
c3[1] = c2[1] + h*cost
c4[0] = c1[0] - h*sint
c4[1] = c1[1] + h*cost
return [list(c1), list(c2), c3, c4]
def thanSet(self, text, c1, c2, perp):
"Sets the attributes of the aligned dimension."
self.text = text
self.cp = self.__rect(c1, c2, perp)
self.perp = perp
xp = [c1[0] for c1 in self.cp]
yp = [c1[1] for c1 in self.cp]
self.setBoundBox([min(xp), min(yp), max(xp), max(yp)])
# self.thanTags = () # thanTags is initialised in ThanElement
def thanIsNormal(self):
"Returns False if the the aligned dimension is degenerate (it is blank)."
return not thanNear2(cp[0], cp[1]) # Degenerate dimension
def thanClone(self):
"Makes a geometric clone of itself."
el = ThanDimali()
el.thanSet(self.text, self.cp[0], self.cp[1], self.perp)
return el
def thanRotate(self):
"Rotates the element within XY-plane with predefined angle and rotation angle."
self.thanRotateXyn(self.cp)
xp = [c1[0] for c1 in self.cp]
yp = [c1[1] for c1 in self.cp]
self.setBoundBox([min(xp), min(yp), max(xp), max(yp)])
def thanMirror(self):
"Mirrors the element within XY-plane with predefined point and unit vector."
self.thanMirrorXyn(self.cp)
xp = [c1[0] for c1 in self.cp]
yp = [c1[1] for c1 in self.cp]
self.setBoundBox([min(xp), min(yp), max(xp), max(yp)])
def thanScale(self, cs, scale):
"Scales the element in n-space with defined scale and center of scale."
for cc in self.cp:
cc[:] = [cs1+(cc1-cs1)*scale for (cc1,cs1) in izip(cc, cs)]
cscs = [cs[0], cs[1], cs[0], cs[1]]
self.thanXymm[:] = [cs1+(cc1-cs1)*scale for (cc1,cs1) in izip(self.thanXymm, cscs)]
def thanMove(self, dc):
"Moves the element with defined n-dimensional distance."
for cc in self.cp:
cc[:] = [cc1+dd1 for (cc1,dd1) in izip(cc, dc)]
dcdc = [dc[0], dc[1], dc[0], dc[1]]
self.thanXymm[:] = [cc1+dd1 for (cc1,dd1) in izip(self.thanXymm, dcdc)]
def thanOsnap(self, proj, otypes, ccu, eother, cori):
"Return a point of type otype nearest to xcu, ycu."
if "ena" not in otypes: return None # Object snap is disabled
ps = []
if "end" in otypes: # type "end" without type "int"
for c in self.cp[2:]:
ps.append((fabs(c[0]-ccu[0])+fabs(c[1]-ccu[1]), "end", c))
if len(ps) > 0: return min(ps)
return None
def thanBreak(self, c1=None, c2=None):
"Just inform that the aligned dimension can not (yet) be broken."
return False # Break is NOT implemented
def thanLength(self):
"Returns the true length (distance) of the dimension."
ca = self.cp[0]
cb = self.cp[1]
return hypot(cb[0]-ca[0], cb[1]-ca[1])
def thanArea(self):
"Just inform that the aligned dimension does not have area."
return None
def thanTkGet(self, proj):
"Gets the attributes of the text interactively from a window."
un = proj[1].thanUnits
c1 = proj[2].thanGudGetPoint(T["First dimension point: "])
if c1 == Canc: return Canc # Aligned dimension cancelled
statonce = ""
while True:
c2 = proj[2].thanGudGetLine(c1, T["Last dimension point: "], statonce=statonce)
if c2 == Canc: return Canc # Aligned dimension cancelled
if not thanNear2(c1, c2): break
statonce = T["Degenerate dimension. Try again.\n"]
cost = c2[0]-c1[0]; sint = c2[1]-c1[1]
w = hypot(cost, sint)
cost /= w; sint /= w
dis = "%.2f" % w
mes = "%s (enter=%s): " % (T["Dimension Text"], dis)
text = proj[2].thanGudGetText(mes, dis)
if text == Canc: return Canc # text cancelled
# perp = proj[2].thanGudGetFloat(T["Perpendicular location: "], 0.00)
# if perp == Canc: return Canc # text cancelled
self.thanSet(text, c1, c2, 0.0)
self.thanTags = ("e0", ) # So that we know that it is temporary
ct = [(t1+t2)*0.5 for t1,t2 in zip(c1,c2)]
t = [0.0]*len(c1)
t[:2] = -sint, cost
c3 = proj[2].thanGudGetMovend(ct, "Perpendicular location: ", elems=[self], direction=t)
if c3 == Canc: return Canc # Aligned dimension cancelled
perp = (c3[0]-ct[0])*(-sint) + (c3[1]-ct[1])*cost
self.thanSet(text, c1, c2, perp)
return True # Text OK
ticksize = 0.15
textsize = 0.20
dimscale = 10.0
def thanTkDraw(self, than):
"Draws rotated text in ThanCad line font."
if thanNear2(self.cp[0], self.cp[1]): return # degenerate - do not draw
c1 = self.cp[3]
c2 = self.cp[2]
cost = c2[0]-c1[0]
sint = c2[1]-c1[1]
theta = atan2(sint, cost)
w = hypot(cost, sint)
ThanElement.thanRotateSet(c1, theta)
temp = ThanText()
temp.thanSet(self.text, c1, self.textsize*self.dimscale, 0.0)
dc = [0.0] * len(c1)
dc[0] += (w-temp.w1)*0.5
dc[1] -= temp.h1*0.5
temp.thanMove(dc)
temp.thanRotate()
temp.thanTags = self.thanTags
temp.thanTkDraw(than)
ct = list(c1)
ct1 = list(ct)
ct1[0] += self.ticksize*self.dimscale
ct1[1] -= self.ticksize*self.dimscale*0.5
ct2 = list(ct1)
ct2[1] += self.ticksize*self.dimscale
cline = [[ct1, ct, ct2]]
ct = list(c1)
ct2 = list(ct)
ct2[0] += (w-temp.w1-temp.h1)*0.5
cline.append([list(ct), ct2])
ct = list(c1)
ct[0] += w
ct1 = list(ct)
ct1[0] -= (w-temp.w1-temp.h1)*0.5
cline.append([ct1, ct])
ct = list(ct)
ct1 = list(ct)
ct1[0] -= self.ticksize*self.dimscale
ct1[1] -= self.ticksize*self.dimscale*0.5
ct2 = list(ct1)
ct2[1] += self.ticksize*self.dimscale
cline.append([ct1, ct, ct2])
for cp in cline:
temp = ThanLine()
temp.thanSet(cp)
temp.thanRotate()
temp.thanTags = self.thanTags
temp.thanTkDraw(than)
def thanTkHiwin(self, than):
"Highlights with a (small) window very small elements so that they become visible."
self.thanTkHiwinDo(than, self.thanLength(), self.cp[0])
def thanExpDxf(self, fDxf):
"Exports the text to dxf file."
fDxf.thanDxfPlotSymbol3(self.c1[0], self.c1[1], self.c1[2], self.size, self.text, self.theta)
def thanExpPilother(self, than):
"Exports the text to a PIL raster image."
x1, y1 = than.ct.global2Locali(self.x1, self.y1)
than.dc.text((x1, y1), self.text, font=than.font, fill=than.outline)
def thanExpPil(self, than):
"Exports rotated text (in ThanCad line font) into a PIL raster image."
xa, ya = than.ct.global2Local(self.c1[0], self.c1[1])
w, h = than.ct.global2LocalRel(self.w1, -self.h1) # Ensure h>0 (local y-axis is positive downwards)
if h < 1: return # Size too small to be seen; draw rectangle instead
than.thanFont.thanPilPaint(than, xa, ya, h, self.text, self.theta)
def thanPlotPdf(self, than):
"Plots rotated text (in ThanCad line font) into a PDF file."
xa, ya = than.ct.global2Local(self.c1[0], self.c1[1])
w, h = than.ct.global2LocalRel(self.w1, self.h1) # Ensure h>0 (local y-axis is positive upwards)
if h < 0.05: return # Size too small to be seen; draw rectangle instead
lines = than.thanFont.than2lines(xa, ya, h, self.text, self.theta, mirrory=True)
lineto = pyx.path.lineto
moveto = pyx.path.moveto
closepath = pyx.path.closepath
for cp in lines:
if len(cp) < 2: continue
if len(cp) == 2:
ca = cp[0][0], cp[0][1]
cb = cp[1][0], cp[1][1]
p = pyx.path.line(ca[0], ca[1], cb[0], cb[1])
else:
xy1 = [lineto(c1[0], c1[1]) for c1 in islice(cp, 1, None)]
xy1.insert(0, moveto(cp[0][0], cp[0][1]))
if cp[0] == cp[-1]: xy1[-1] = closepath()
p = pyx.path.path(*xy1)
than.dc.stroke(p)
def thanList(self, than):
"Shows information about the the aligned dimension element."
than.writecom("Element: DIMALI")
than.write(" Layer: % s\n" % than.laypath)
t = ('Value: "%s" Real length=%s' % (self.text, than.strdis(self.thanLength())),
"Dimension reference: %s -:- %s" % (than.strcoo(self.cp[0]), than.strcoo(self.cp[1])),
"Dimension perpendicular location: %s\n" % than.strdis(self.perp),
)
than.write("\n".join(t))
if __name__ == "__main__":
print __doc__
|