/* Useful functions for drawing Physics 101 figures. * * Copyright (C) 2008-2009 W. Trevor King * * 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. * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ import geometry; // ---------------------- Mass ------------------------- struct Mass { pair center; real m; real radius; pen outline; pen fill; Label L; void operator init(pair center=(0,0), real m=1, real radius=2mm, pen outline=currentpen, pen fill=grey, Label L="") { this.center = center; this.m = m; this.radius = radius; this.outline = outline; this.fill = fill; this.L = L; } void draw(picture pic=currentpicture) { picture picF; path c = scale(radius)*unitcircle; filldraw(picF, c, fill, outline); label(pic=picF, L=L, position=(0,0)); add(pic, picF, center); } } struct Block { pair center; real m; real width; real height; real direction; pen outline; pen fill; Label L; void operator init(pair center=(0,0), real m=1, real width=5mm, real height=-1, real direction=0, pen outline=currentpen, pen fill=grey, Label L="") { this.center = center; this.m = m; this.width = width; if (height == -1) this.height = width; else this.height = height; this.direction = direction; this.outline = outline; this.fill = fill; this.L = L; } void draw(picture pic=currentpicture) { picture picF; path c = rotate(direction)*scale(width,height)*shift(-.5,-.5)*unitsquare; filldraw(picF, c, fill, outline); label(pic=picF, L=L, position=(0,0)); add(pic, picF, center); } } // ---------------------- Vectors ------------------------- struct Vector { pair center; real mag; real dir; // angle in the plane of the drawing. real phi; // angle with the plane of the drawing, 90 is out of the page. pen outline; Label L; real out_of_plane_radius; real out_of_plane_tolerance; void operator init(pair center=(0,0), real mag=5mm, real dir=0, real phi=0, pen outline=currentpen, Label L="") { this.center = center; this.mag = mag; this.dir = dir; this.phi = phi; this.outline = outline; this.L = L; this.out_of_plane_radius = 1mm; this.out_of_plane_tolerance = 0.01; } pair pTip() { return this.mag*dir(this.dir)+this.center; } void draw(picture pic=currentpicture, bool rotateLabel=false, pair labelOffset=(0,0)) { picture picF; pair p = (0,0); pair label_rotate = (1,0); pair label_align = (0,1); real phi_e = this.phi % 360; // effective phi if (this.mag < 0) (phi_e + 180) % 360; if (Tan(phi_e) == 0 || abs(1.0/Tan(phi_e)) > this.out_of_plane_tolerance) { // draw arrow in the plane of the drawing // TODO: thickening for phi? p = this.mag*Cos(this.phi)*dir(this.dir); path P = (0,0)--p; draw(picF, P, outline, Arrow); if (rotateLabel == true) label_rotate = p; label_align = unit(rotate(90)*p); } else if (phi_e > 0 && phi_e < 180) { // draw a circled dot for out-of-the-page p = (0, this.out_of_plane_radius); draw(picF, scale(this.out_of_plane_radius)*unitcircle, outline); dot(picF, (0,0), outline); } else { // draw a circled cross for into-the-page real a = 0.8*sqrt(2.0)/2.0; p = (0, this.out_of_plane_radius); draw(picF, scale(this.out_of_plane_radius)*unitcircle, outline); draw(picF, scale(this.out_of_plane_radius)*((-a,-a)--(a,a)), outline); draw(picF, scale(this.out_of_plane_radius)*((-a,a)--(a,-a)), outline); } label(pic = picF, L = rotate(degrees(label_rotate)) * L, position = p+labelOffset, align = label_align); add(pic, picF, center); } } Vector Velocity(pair center=(0,0), real mag=5mm, real dir=0, real phi=0, Label L="") { Vector v = Vector(center=center, mag=mag, dir=dir, phi=phi, L=L, outline=rgb(1,0.1,0.2)); // red return v; } // ---------------------- Forces ------------------------- Vector Force(pair center=(0,0), real mag=5mm, real dir=0, real phi=0, Label L="") { Vector v = Vector(center=center, mag=mag, dir=dir, phi=phi, L=L, outline=rgb(0.1,0.2,1)); // blue return v; } // ---------------------- Measures ------------------------- // Distance derived from CAD.MeasuredLine struct Distance { pair pFrom; pair pTo; real offset; real scale; pen outline; Label L; void operator init(pair pFrom=(0,0), pair pTo=(5mm,0), real offset=0, real scale=5mm, pen outline=currentpen, Label L="") { this.pFrom = pFrom; this.pTo = pTo; this.offset = offset; this.scale = scale; this.outline = outline; this.L = L; } void draw(picture pic=currentpicture, bool rotateLabel=true, real labelangle=90, real labeloffset=-1) { picture picF; pair pDiff = pTo - pFrom; if (labeloffset == -1) { picture picL; label(picL, L); pair pLabelSize = 1.2 * (max(picL)-min(picL)); labeloffset = pLabelSize.y/2; } path p = (0,0)--pDiff; pair label_rotate=pDiff; if (rotateLabel == false) label_rotate=(1,0); draw(picF, p, outline, Arrows); label(pic = picF, L = rotate(degrees(label_rotate)) * L, position = pDiff/2 + unit(rotate(labelangle)*pDiff) * labeloffset); //label(pic=picF, L = rotate(degrees(label_rotate)) format("%g", pDiff/scale), position = TODO); add(pic, picF, pFrom+offset*unit(rotate(-90)*pDiff)); } } struct Angle { pair B; pair A; // center of angle pair C; real radius; // radius < 0 for exterior angles. pen outline; Label L; void operator init(pair B, pair A, pair C, real radius=5mm, pen outline=currentpen, Label L="") { this.B = B; this.A = A; this.C = C; this.radius = radius; this.outline = outline; this.L = L; } void draw(picture pic=currentpicture, bool rotateLabel=false, real labelOffsetAdjustment=0) { picture picF; picture picL; bool direction; label(picL, L); pair pLabelSize = 1.2 * (max(picL)-min(picL)); real ccw_angle = degrees(C-A)-degrees(B-A); bool direction = CCW; if (ccw_angle < 0) ccw_angle += 360.0; if (ccw_angle > 180) direction = CW; if (radius < 0) direction = !direction; path p = arc((0,0), fabs(radius), degrees(B-A), degrees(C-A), direction); real t = reltime(p, 0.5); pair P = midpoint(p); pair tangent = dir(p, t); if (direction == CW) tangent *= -1.0; pair label_rotate = tangent; if (rotateLabel == false) label_rotate = (1,0); draw(picF, p, outline); label(pic = picF, L = rotate(degrees(label_rotate)) * L, position = P + unit(P) * (pLabelSize.y / 2 + labelOffsetAdjustment)); add(pic, picF, A); } } Vector hatVect (string name, pair center=(0,0), real dir=0, real phi=0) { string L = replace("$\mathbf{\hat{X}}$", "X", name); Vector v = Vector(center=center, mag=5mm, dir=dir, phi=phi, L=L, outline=rgb(0,0,0)); return v; } Vector ihat (pair center=(0,0), real dir=0, real phi=0) { Vector v = hatVect(name="i", center=center, dir=dir, phi=phi); return v; } Vector jhat (pair center=(0,0), real dir=90, real phi=0) { Vector v = hatVect(name="j", center=center, dir=dir, phi=phi); return v; } void draw_ijhat(pair center=(0,0), real idir=0) { Vector ihat = ihat(center, idir); Vector jhat = jhat(center, idir+90); ihat.draw(); jhat.draw(); } // ---------------------- Shapes ------------------------- struct Wire { pair pFrom; pair pTo; pen outline; Label L; void operator init(pair pFrom=(0,0), pair pTo=(5mm,0), pen outline=currentpen, Label L="") { this.pFrom = pFrom; this.pTo = pTo; this.outline = outline; this.L = L; } void draw(picture pic=currentpicture, bool rotateLabel=true) { picture picF; picture picL; label(picL, L); pair pLabelSize = 1.2 * (max(picL)-min(picL)); pair pDiff = pTo - pFrom; path p = (0,0)--pDiff; pair label_rotate=pDiff; if (rotateLabel == false) label_rotate=(1,0); draw(picF, p, outline); label(pic = picF, L = rotate(degrees(label_rotate)) * L, position = pDiff/2 + unit(rotate(90)*pDiff) * pLabelSize.y / 2); add(pic, picF, pFrom); } } struct Surface { pair pFrom; pair pTo; real thickness; pen outline; pen filla; pen fillb; Label L; void operator init(pair pFrom=(0,0), pair pTo=(5mm,0), real thickness=5mm, pen outline=currentpen, pen filla=rgb(.5,.5,.5), pen fillb=rgb(.7,.7,.7), Label L="") { this.pFrom = pFrom; this.pTo = pTo; this.thickness = thickness; this.outline = outline; this.filla = filla; this.fillb = fillb; this.L = L; } void draw(picture pic=currentpicture, bool rotateLabel=true) { picture picF; picture picL; label(picL, L); pair pLabelSize = 1.2 * (max(picL)-min(picL)); pair pDiff = pTo - pFrom; pair pDepth = rotate(-90)*unit(pDiff)*thickness; path p = (0,0) -- pDiff -- (pDiff+pDepth) -- pDepth -- cycle; pair label_rotate=pDiff; if (rotateLabel == false) label_rotate=(1,0); axialshade(pic=picF, g=p, pena=filla, a=(0,0), penb=fillb, b=pDepth); draw(picF, p, outline); label(pic = picF, L = rotate(degrees(label_rotate)) * L, position = (pDiff+pDepth)/2); add(pic, picF, pFrom); } } struct Spring { pair pFrom; pair pTo; real k; real width; real dLength; // length of a single loop (when unstretched) real deadLength; // length before loops start on each end real unstretchedLength; int nLoops; pen outline; Label L; void operator init(pair pFrom=(0,0), pair pTo=(5mm,0), real k=1, real width=3mm, real dLength=1mm, real deadLength=3mm, real unstretchedLength=12mm, pen outline=currentpen, Label L="") { this.pFrom = pFrom; this.pTo = pTo; this.k = k; this.width = width; this.dLength = dLength; this.unstretchedLength = unstretchedLength; this.outline = outline; this.L = L; this.nLoops = floor((unstretchedLength-2*deadLength)/dLength); if (this.nLoops == 0) this.nLoops = 1; this.deadLength = (unstretchedLength-this.nLoops*dLength)/2; } void draw(picture pic=currentpicture, bool rotateLabel=true) { picture picF; picture picL; label(picL, L); pair pLabelSize = 1.2 * (max(picL)-min(picL)); pair pDiff = pTo - pFrom; real working_dLength = (length(pDiff) - 2*deadLength) / nLoops; path p = (0,0)--(deadLength,0); path loop = (0,0) --(working_dLength/4, width/2) --(working_dLength*3/4, -width/2) --(working_dLength, 0); pair loopStart; for (int i=0; i