diff options
Diffstat (limited to 'Master/texmf-dist/metapost')
-rw-r--r-- | Master/texmf-dist/metapost/byrne/byrne.mp | 3852 |
1 files changed, 3852 insertions, 0 deletions
diff --git a/Master/texmf-dist/metapost/byrne/byrne.mp b/Master/texmf-dist/metapost/byrne/byrne.mp new file mode 100644 index 00000000000..115998e257a --- /dev/null +++ b/Master/texmf-dist/metapost/byrne/byrne.mp @@ -0,0 +1,3852 @@ +% byrne-euclid 0.2.2 +% MetaPost library that implements most of the graphical +% features present in Oliver Byrne's version of Euclid's "Elements" +% Copyright 2022 Sergey Slyusarev +% +% 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 3 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, see <http://www.gnu.org/licenses/>. + +% +% Here we define some things of general interest +% + +pi := 3.1415926; +radian := 180/pi; +lineWidth := 2pt; +lineWidthThin := 1pt; +lineWidthHair := 1/2pt; +pointMarkSize := 4pt; +pointLinesSize := 1/2cm; +defaultScaleFactor := 1; +angleSize := 1cm; +angleScale := 1; +globalRotation := 0; +magnitudeSize := 5/18cm; +magnitudeScale := 1; +magnitudeGap := 3/2lineWidth; +markLength := 3lineWidth; +rayExtension := 1/3cm; + +boolean textLabels, ghostLines, autoRightAngles, omitDuplicateTextLabels, autoLabelingMode, mainPictureMode; + +textLabels := false; +ghostLines := true; +autoRightAngles := false; +omitDuplicateTextLabels := false; +textLabelShift := lineWidth; +autoLabelingMode := false; +mainPictureMode := false; + +boolean solidAngleMode; + +solidAngleMode := false; + +numeric dpLength[]; +dpLength0 := 2lineWidth; +dpLength1 := 2lineWidth; + +vardef sin primary x = (sind(x*radian)) enddef; + +vardef cos primary x = (cosd(x*radian)) enddef; + +vardef arcsind primary x = angle((1+-+x,x)) enddef; + +vardef arccosd primary x = angle((x,1+-+x)) enddef; + +vardef pathToString (expr p) = +save outputString, endpoint, i; + string outputString; + if (length(p) > 0): + outputString := + "(" & decimal(xpart(point 0 of p)) & ", " & decimal(ypart(point 0 of p)) & ")" + & ".. controls (" & decimal(xpart(postcontrol 0 of p)) & ", " & decimal(ypart(postcontrol 0 of p)) & ") and " + & "(" & decimal(xpart(precontrol 1 of p)) & ", " & decimal(ypart(precontrol 1 of p)) & ")"; + if (cycle p): + endpoint := length(p) - 1; + else: + endpoint := length(p); + fi; + for i := 1 step 1 until endpoint: + outputString := outputString + & ".. (" & decimal(xpart(point i of p)) & ", " & decimal(ypart(point i of p)) & ")"; + if ((i < length(p)) or (cycle p)): + outputString := outputString + & ".. controls (" & decimal(xpart(postcontrol i of p)) & ", " & decimal(ypart(postcontrol i of p)) & ") and " + & "(" & decimal(xpart(precontrol i + 1 of p)) & ", " & decimal(ypart(precontrol i + 1 of p)) & ")"; + fi; + endfor; + if (cycle p): + outputString := outputString & " .. cycle"; + fi; + else: + outputString := + "(" & decimal(xpart(point 0 of p)) & ", " & decimal(ypart(point 0 of p)) & ")"; + fi; + outputString +enddef; + +color byred, byblue, byyellow, byblack, bygrey, + byredN, byblueN, byyellowN, byblackN, bygreyN, + outlineColor.byredN, outlineColor.byblueN, outlineColor.byyellowN, outlineColor.byblackN, outlineColor.bygreyN, + bytransparent, byDefaultAngleOptionalColor; +byred := (.85, .3, .1); +byblue := (.15, .35, .6); +byyellow := (.95, .7, 0.1); +byblack := black; +bygrey := (.8, .8, .8); +byredN := white; +byblueN := white; +byyellowN := white; +byblackN := white; +bygreyN := white; +outlineColor.byredN := byred; +outlineColor.byblueN := byblue; +outlineColor.byyellowN := byyellow; +outlineColor.byblackN := byblack; +outlineColor.bygreyN := bygrey; +bytransparent := 0.99white; + +byDefaultAngleOptionalColor := white; + +linecap := butt; + +vardef typeOf (expr v) = + save determinedType; + string determinedType; + determinedType := "undefined"; + if (boolean v): + determinedType := "boolean"; + elseif (cmykcolor v): + determinedType := "cmykcolor"; + elseif (color v): + determinedType := "color"; + elseif (rgbcolor v): + determinedType := "rgbcolor"; + elseif (numeric v): + determinedType := "numeric"; + elseif (pair v): + determinedType := "pair"; + elseif (path v): + determinedType := "path"; + elseif (pen v): + determinedType := "pen"; + elseif (picture v): + determinedType := "picture"; + elseif (string v): + determinedType := "string"; + elseif (transform v): + determinedType := "transform"; + fi; + determinedType +enddef; + +vardef sign (expr x) = + if x > 0: 1 fi + if x < 0: -1 fi + if x = 0: 1 fi +enddef; + +vardef colorLightness (expr col) = +save r, g, b; + if color col: + r := redpart(col); + g := greenpart(col); + b := bluepart(col); + elseif cmykcolor col: + r := (1 - cyanpart(col)) * (1 - blackpart(col)); + g := (1 - magentapart(col)) * (1 - blackpart(col)); + b := (1 - yellowpart(col)) * (1 - blackpart(col)); + fi; + (0.2126*r) + (0.7152*g) + (0.0722*b) % from here https://en.wikipedia.org/wiki/Relative_luminance but it doesn't work this way; to be corrected +enddef; + +vardef isWhite (expr col) = + if (colorLightness(col) = 1): % arbitrary value + true + else: + false + fi +enddef; + +vardef isLight (expr col) = + if (colorLightness(col) > 0.95): % arbitrary value + true + else: + false + fi +enddef; + +vardef defineColor@#(expr col) = + if (typeOf(col) = "color"): + color @#; + else: + cmykcolor @#; + fi; + @# := col; +enddef; + +vardef selectOutlineColor@# = + if (typeOf(outlineColor.@#) = "color") or (typeOf(outlineColor.@#) = "cmykcolor"): + outlineColor.@# + else: + black + fi +enddef; + +defineColor.oiBlack((0, 0, 0)); +defineColor.oiOrange((0.9,0.6,0)); +defineColor.oiSkyBlue((0.35,0.7,0.9)); +defineColor.oiGreen((0,0.6,0.5)); +defineColor.oiYellow((0.95,0.9,0.25)); +defineColor.oiBlue((0,0.45,0.7)); +defineColor.oiVermillion((0.8,0.4,0)); +defineColor.oiPurple((0.8,0.6,0.7)); + +% CMYK variants +%defineColor.oiBlack((0,0,0,1)); +%defineColor.oiOrange((0,0.5,1,0)); +%defineColor.oiSkyBlue((0.8,0,0,0)); +%defineColor.oiGreen((0.97,0,0.75,0)); +%defineColor.oiYellow((0.1,0.05,0.9,0)); +%defineColor.oiBlue((1,0.5,0,0)); +%defineColor.oiVermillion((0,0.8,1,0)); +%defineColor.oiPurple((0.1,0.7,0,0)); + +vardef cycleval (expr v, l) = + save rv; + numeric rv; + if (l > 0): + if ((v mod l) < 0): + rv := l - (v mod l); + else: + rv := (v mod l); + fi; + else: + rv := 0; + fi; + rv +enddef; + +vardef angleOpticalScale (expr v) = + save va; + numeric va; + if (v > 60): + va := 60; + else: + va := v; + fi; + ((va/60)**(-1/2)) +enddef; + +vardef distanceToLine (expr givenPoint, givenLine) = + save p; + pair p[]; + p0 := point 0 of givenLine; + p1 := point 0 of reverse(givenLine); + p2 - givenPoint = whatever * ((p1-p0) rotated 90); + p2 = whatever[p0, p1]; + p3 := p2; + arclength(givenPoint -- p3) +enddef; + +vardef angleValue (expr a, b, c) = + save v; + numeric v; + v := angle(a-b) - angle(c-b); + if (v > 180): v := v - 360; fi; + if (v < -180): v := v + 360; fi; + v +enddef; + +vardef lineThickness (expr th) = + if (th = 0): + lineWidth + elseif (th = 1): + lineWidthThin + elseif (th = 2): + lineWidthHair + else: + lineWidth + fi +enddef; + +vardef byDashPattern (expr l, dp) = + save d, n; + numeric d[], n; + n := ceiling(l/(1/3dpLength[dp])); + if (n mod 3) <> 2: + n := n - (n mod 3) + 2; + fi; + d1 := 2(l/n); + d2 := l/n; + dashpattern (on d1 off d2) +enddef; + +def startTempScale (expr tmpScale) = + begingroup + save scaleFactor; + scaleFactor := tmpScale; +enddef; + +def stopTempScale = + endgroup +enddef; + +def startTempAngleScale (expr tmpAngleScale) = + begingroup + save angleScale; + angleScale := tmpAngleScale; +enddef; + +def stopTempAngleScale = + endgroup +enddef; + +def startGlobalRotation (expr ang) = + begingroup + save globalRotation; + globalRotation := ang; +enddef; + +def stopGlobalRotation = + endgroup +enddef; + +def startAutoLabeling = + begingroup + save autoLabelingMode; + boolean autoLabelingMode; + autoLabelingMode := true; +enddef; + +def stopAutoLabeling = + endgroup +enddef; + +def startOffspringPictureMode = + begingroup + save uniqueTextLabels, omitDuplicateTextLabels; + string uniqueTextLabels; + boolean omitDuplicateTextLabels; + omitDuplicateTextLabels := true; + uniqueTextLabels := ""; +enddef; + +def stopOffspringPictureMode = + endgroup +enddef; + +def startMainPictureMode = + begingroup + save mainPictureMode, ghostPicture; + boolean mainPictureMode; + picture ghostPicture; + ghostPicture := image(); + mainPictureMode := true; +enddef; + +def stopMainPictureMode = + endgroup +enddef; + +picture ghostPicture; +ghostPicture := image(); + +% +% This section is devoted to lists +% + +vardef appendList (suffix listName)(expr valueToAdd, whereToAdd, omitDuplicates) = + if (not string listName) or (unknown listName): + string listName; + listName := ""; + fi; + if length(listName) = 0: + listName := valueToAdd; + else: + save valueExists; + boolean valueExists; + valueExists := false; + if omitDuplicates: + valueExists := isInList (valueToAdd, listName); + fi; + if not valueExists: + if (whereToAdd = 1): + listName := listName & ", " & valueToAdd; + else: + listName := valueToAdd & ", " & listName; + fi; + fi; + fi; +enddef; + +vardef isInList (expr valueToLookFor)(suffix listName) = + save rv, i; + boolean rv; + rv := false; + if (not string listName) or (unknown listName): + string listName; + listName := ""; + fi; + forsuffixes i=scantokens(listName): + if (str i = valueToLookFor): + rv := true; + fi; + endfor; + rv +enddef; + +vardef setAttribute@#(expr attrGenus, attrSpecies, attrName, attrValue) = + save finName; + string finName; + if not string scantokens(attrGenus & "Synonym").scantokens(attrName): + finName := attrName; + else: + finName := scantokens(attrGenus & "Synonym").scantokens(attrName); + if (boolean scantokens(attrGenus & "SynonymPartial").scantokens(attrName)): + if scantokens(attrGenus & "SynonymPartial").scantokens(attrName): + finName := attrName; + fi; + fi; + fi; + if str @# = "": + scantokens(typeOf(attrValue)) scantokens(attrGenus & attrSpecies).scantokens(finName); + elseif (typeOf(scantokens(attrGenus & attrSpecies).scantokens(finName)0) <> typeOf(attrValue)): + scantokens(typeOf(attrValue)) scantokens(attrGenus & attrSpecies).scantokens(finName)[]; + fi; + scantokens(attrGenus & attrSpecies).scantokens(finName)@# := attrValue; +enddef; + +vardef getAttribute@#(expr attrGenus, attrSpecies, attrName) = + save finName; + string finName; + if not string scantokens(attrGenus & "Synonym").scantokens(attrName): + finName := attrName; + else: + finName := scantokens(attrGenus & "Synonym").scantokens(attrName); + if (boolean scantokens(attrGenus & "SynonymPartial").scantokens(attrName)): + if scantokens(attrGenus & "SynonymPartial").scantokens(attrName): + if known scantokens(attrGenus & attrSpecies).scantokens(attrName)@#: + finName := attrName; + fi; + fi; + fi; + fi; + scantokens(attrGenus & attrSpecies).scantokens(finName)@# +enddef; + +vardef attributeExists (expr attrGenus, attrSpecies, attrName) = + save rv; + boolean rv; + if known scantokens(attrGenus & attrSpecies).scantokens(attrName): + rv := true; + else: + rv := false; + fi; + rv +enddef; + +setAttribute("line", "Color", "noLine", white); +setAttribute("line", "Dashed", "noLine", 0); +setAttribute("line", "EndAName", "noLine", "noLine"); +setAttribute("line", "EndBName", "noLine", "noLine"); +setAttribute("line", "EndA", "noLine", inf); +setAttribute("line", "EndB", "noLine", inf); +setAttribute("line", "Synonym", "noLine", "noLine"); + +setAttribute("line", "Color", "dummyLine", bygrey); +setAttribute("line", "Dashed", "dummyLine", 0); +setAttribute("line", "Thin", "dummyLine", 1); + +% +% Some general 3D stuff +% + +color projectionAngle; +projectionAngle := (0, 0, 0); + +color rotationAxisB.X, rotationAxisE.X, rotationAxisB.Y, rotationAxisE.Y, rotationAxisB.Z, rotationAxisE.Z; +string rotationAxisPart.X, rotationAxisPart.Y, rotationAxisPart.Z; +rotationAxisB.X := (0, 0, 0); +rotationAxisE.X := (1, 0, 0); +rotationAxisPart.X := "redpart"; +rotationAxisB.Y := (0, 0, 0); +rotationAxisE.Y := (0, 1, 0); +rotationAxisPart.Y := "greenpart"; +rotationAxisB.Z := (0, 0, 0); +rotationAxisE.Z := (0, 0, 1); +rotationAxisPart.Z := "bluepart"; + +primarydef somecolor colorrotated someangle = + rotateColor(somecolor, someangle) +enddef; + +% Rotation order can be selected like this rotateColor.XYZ, +% or by setting defaultRotationOrder + +string defaultRotationOrder; +defaultRotationOrder := "ZYX"; + +vardef rotateColor@#(expr somecolor, someangle) = + save rotationOrder, argumentString, rv; + string rotationOrder[], argumentString; + color rv; + argumentString := str @#; + if argumentString = "": + argumentString := defaultRotationOrder; + fi; + for i := 0 step 1 until length(argumentString) - 1: + rotationOrder[i] = substring (i, i + 1) of argumentString; + endfor; + rv := somecolor; + rv := byRotateAroundAxis( + rotationAxisB.scantokens(rotationOrder0), + rotationAxisE.scantokens(rotationOrder0), + scantokens(rotationAxisPart.scantokens(rotationOrder0))(someangle), rv); + rv := byRotateAroundAxis( + rotationAxisB.scantokens(rotationOrder1), + rotationAxisE.scantokens(rotationOrder1), + scantokens(rotationAxisPart.scantokens(rotationOrder1))(someangle), rv); + rv := byRotateAroundAxis( + rotationAxisB.scantokens(rotationOrder2), + rotationAxisE.scantokens(rotationOrder2), + scantokens(rotationAxisPart.scantokens(rotationOrder2))(someangle), rv); + rv +enddef; + +primarydef colorone crossproduct colortwo = +begingroup + save xp, yp, zp; + numeric xp[], yp[], zp[]; + xp1 := (redpart colorone)/cm; + yp1 := (greenpart colorone)/cm; + zp1 := (bluepart colorone)/cm; + xp2 := (redpart colortwo)/cm; + yp2 := (greenpart colortwo)/cm; + zp2 := (bluepart colortwo)/cm; + xp3 := ((yp1*zp2) - (zp1*yp2))*cm; + yp3 := ((zp1*xp2) - (xp1*zp2))*cm; + zp3 := ((xp1*yp2) - (yp1*xp2))*cm; + (xp3, yp3, zp3) +endgroup +enddef; + +primarydef colorone dotprodXYZ colortwo = +begingroup + save xp, yp, zp; + numeric xp[], yp[], zp[]; + xp1 := (redpart colorone); + yp1 := (greenpart colorone); + zp1 := (bluepart colorone); + xp2 := (redpart colortwo); + yp2 := (greenpart colortwo); + zp2 := (bluepart colortwo); + xp1*xp2 + yp1*yp2 + zp1*zp2 +endgroup +enddef; + +vardef isInPlane (expr colPoint, colPlaneA, colPlaneB, colPlaneC) = + save vecsToCompare, rv; + color vecsToCompare[]; + boolean rv; + rv := false; + if (colPoint = colPlaneA) + or (colPoint = colPlaneB) + or (colPoint = colPlaneC): + rv := true; + else: + vecsToCompare1 := colPlaneA-colPlaneB; + vecsToCompare2 := colPlaneC-colPlaneB; + vecsToCompare3 := colPoint-colPlaneB; + fi; + if not rv: + if ((absXYZ(unitvectorXYZ(vecsToCompare1) - unitvectorXYZ(vecsToCompare3)) < 1/100) + or (absXYZ(unitvectorXYZ(vecsToCompare1) + unitvectorXYZ(vecsToCompare3)) < 1/100) + or (absXYZ(unitvectorXYZ(vecsToCompare2) - unitvectorXYZ(vecsToCompare3)) < 1/100) + or (absXYZ(unitvectorXYZ(vecsToCompare2) + unitvectorXYZ(vecsToCompare3)) < 1/100)): + rv := true; + else: + vecsToCompare4 := unitvectorXYZ(vecsToCompare1 crossproduct vecsToCompare2); + vecsToCompare5 := unitvectorXYZ(vecsToCompare3 crossproduct vecsToCompare2); + if ((absXYZ(vecsToCompare4 - vecsToCompare5) < 1/100) + or (absXYZ(vecsToCompare4 + vecsToCompare5) < 1/100)): + rv := true; + else: + rv := false; + fi; + fi; + fi; + rv +enddef; + +vardef unitvectorXYZ (expr somecolor) = + save vecLength; + numeric vecLength; + vecLength := absXYZ(somecolor); + if (vecLength > 0): + somecolor/vecLength + else: + errmessage("Can't construct a unit vector from a zero-length vector"); + fi +enddef; + +vardef absXYZ (expr somecolor) = + (sqrt( + ((redpart somecolor)/1cm)**2 + + ((greenpart somecolor)/1cm)**2 + + ((bluepart somecolor)/1cm)**2)*1cm) +enddef; + +vardef lineAndPlaneIntersection (expr lineA, lineB, planeA, planeB, planeC) = + save planeNormal, planeOrigin, lineD; + color planeNormal, planeOrigin; + numeric lineD; + planeNormal := unitvectorXYZ((planeA-planeB) crossproduct (planeC-planeB)); + planeOrigin := planeB; + lineD := ((planeNormal/cm) dotprodXYZ ((planeOrigin - lineA)/cm)) + /((planeNormal/cm) dotprodXYZ ((lineB - lineA)/cm)); + lineA + ((lineB-lineA)*lineD) +enddef; + +vardef spaceRotated (suffix somepath)(expr someangle) = + save rv, tv, spacepoint, op, rp, tp; + path rv, tv; + color spacepoint[]; + pair op[], rp[], tp[]; + if (typeOf(thirdDimension.somepath) <> "path"): + path thirdDimension.somepath; + thirdDimension.somepath := somepath scaled 0; + fi; + for i := 0 step 1 until length(somepath): + op0 := point i of somepath; + spacepoint0 := + (xpart(op0), ypart(op0), + xpart(point i of thirdDimension.somepath) + ) colorrotated someangle; + rp0 := (redpart(spacepoint0), greenpart(spacepoint0)); + tp0 := (bluepart(spacepoint0), 0); + if (i = 0): + rv := rp0; + tv := tp0; + else: + op1 := postcontrol (i-1) of somepath; + spacepoint1 := + (xpart(op1), ypart(op1), + xpart(postcontrol (i-1) of thirdDimension.somepath) + ) colorrotated someangle; + rp1 := (redpart(spacepoint1), greenpart(spacepoint1)); + tp1 := (bluepart(spacepoint1), 0); + op2 := precontrol i of somepath; + spacepoint2 := + (xpart(op2), ypart(op2), + xpart(precontrol i of thirdDimension.somepath) + ) colorrotated someangle; + rp2 := (redpart(spacepoint2), greenpart(spacepoint2)); + tp2 := (bluepart(spacepoint2), 0); + rv := rv .. controls rp1 and rp2 .. + if ((i < length(somepath)) or (not cycle somepath)): + rp0 + else: + cycle + fi; + tv := tv .. controls tp1 and tp2 .. + if ((i < length(somepath)) or (not cycle somepath)): + tp0 + else: + cycle + fi; + fi; + endfor; + thirdDimension.somepath := tv; + rv +enddef; + +vardef bySetProjection@#(expr xa, ya, za) = + projectionAngle := (xa, ya, za); + byRotatePoints@#(xa, ya, za, false)(); + if str @# <> "": + defaultRotationOrder := str @#; + fi; +enddef; + +vardef byPutPointsInSpace (text pL) = + byRotatePoints(0, 0, 0, true)(pL); +enddef; + +% Gets a either a 2d or a 3d point and returns a 3d point + +vardef byGetPointXYZ (suffix givenPoint) = + if (typeOf(givenPoint) = "color"): + givenPoint + elseif (typeOf(pointXYZ.givenPoint) = "color"): + pointXYZ.givenPoint + else: + errmessage("Point " & str givenPoint & " has no representation in space"); + fi +enddef; + +vardef byRotateAroundAxis (expr lineEndA, lineEndB, a, pointA) = + save vecA, vecB, rV, q; + color vecA, vecB, rV; + cmykcolor q[]; + vecA := pointA - lineEndA; + vecB := unitvectorXYZ(lineEndB - lineEndA); + q1 := (0, redpart(vecA), greenpart(vecA), bluepart(vecA)); + q2 := (cosd(a/2), sind(a/2)*redpart(vecB), sind(a/2)*greenpart(vecB), sind(a/2)*bluepart(vecB)); + q3 := (cyanpart(q2), -magentapart(q2), -yellowpart(q2), -blackpart(q2)); + q4 := (q2 hamiltonProduct q1) hamiltonProduct q3; + rV := (magentapart(q4), yellowpart(q4), blackpart(q4)) + lineEndA; + rV +enddef; + +vardef byRotatePointsAroundAxis (suffix lineEndA, lineEndB)(expr a)(text pointsList) = + save lineEndAxyz, lineEndBxyz, pN; + color lineEndAxyz, lineEndBxyz; + if (typeOf(lineEndA) = "color"): + lineEndAxyz := lineEndA; + else: + byPutPointsInSpace(lineEndA); + lineEndAxyz := pointXYZ.lineEndA; + fi; + if (typeOf(lineEndB) = "color"): + lineEndBxyz := lineEndB; + else: + byPutPointsInSpace(lineEndB); + lineEndBxyz := pointXYZ.lineEndB; + fi; + forsuffixes pN := pointsList: + if (typeOf(pN) = "color"): + pN := byRotateAroundAxis(lineEndAxyz, lineEndBxyz, a, pN); + elseif (typeOf(pN) = "pair"): + byPutPointsInSpace(pN); + pointXYZ.pN := byRotateAroundAxis(lineEndAxyz, lineEndBxyz, a, pointXYZ.pN); + fi; + endfor; +enddef; + +primarydef colorone hamiltonProduct colortwo = +begingroup + save a, b, c, d; + numeric a[], b[], c[], d[]; + a1 := cyanpart(colorone); + b1 := magentapart(colorone); + c1 := yellowpart(colorone); + d1 := blackpart(colorone); + a2 := cyanpart(colortwo); + b2 := magentapart(colortwo); + c2 := yellowpart(colortwo); + d2 := blackpart(colortwo); + ( + (a1*a2) - (b1*b2) - (c1*c2) - (d1*d2), + (a1*b2) + (b1*a2) + (c1*d2) - (d1*c2), + (a1*c2) - (b1*d2) + (c1*a2) + (d1*b2), + (a1*d2) + (b1*c2) - (c1*b2) + (d1*a2) + ) +endgroup +enddef; + +vardef isRightAngle (suffix a, b, c) = + save d, rv; + numeric d; + boolean rv; + if (attributeExists("point", "XYZ", str a) + and attributeExists("point", "XYZ", str b) + and attributeExists("point", "XYZ", str c)): + d := unitvectorXYZ(pointXYZ.a - pointXYZ.b) dotprodXYZ unitvectorXYZ(pointXYZ.c - pointXYZ.b); + else: + d := unitvector(a - b) dotprod unitvector(c - b); + fi; + if (abs(d) < 1/1000): + rv := true; + else: + rv := false; + fi; + rv +enddef; + +% +% This section is dedicated to lines +% + +vardef byLineRender (expr a, b, col, dp, th, c, d, ct, dt, s, sf) = + save p, la, lineItself, lineBleeding, clippingPath, cutAngle, linePerp, lineStyShift, currentLineWidth, clippingPathPoint, aS, bS; + picture p; + path lineItself, lineBleeding, clippingPath; + pair cutAngle[], linePerp, lineStyShift, clippingPathPoint[], aS, bS; + la := angle(b-a); + aS := a*sf; + bS := b*sf; + lineStyShift := (0, 0); + currentLineWidth := lineThickness(th); + cutAngle1 := unitvector(b-a) rotated -90; + if (abs(c-a) > 0): + if (abs(unitvector(c-a)-unitvector(a-b)) > 1/100): + if (ct = 0): + cutAngle1 := unitvector(1/2[unitvector(b-a), unitvector(c-a)]); + elseif (ct = 1): + cutAngle1 := unitvector(1[unitvector(b-a), unitvector(c-a)]); + fi; + fi; + fi; + cutAngle2 := unitvector(a-b) rotated 90; + if (abs(d-b) > 0): + if (abs(unitvector(d-b)-unitvector(b-a)) > 1/100): + if (dt = 0): + cutAngle2 := unitvector(1/2[unitvector(a-b), unitvector(d-b)]); + elseif (dt = 1): + cutAngle2 := unitvector(1[unitvector(a-b), unitvector(d-b)]); + fi; + fi; + fi; + if (sign(ypart(cutAngle2 rotated -la)) <> sign(ypart(cutAngle1 rotated -la))): + cutAngle2 := cutAngle2 rotated 180; + fi; + lineItself := (a--b) scaled sf; + linePerp := unitvector((point 0 of lineItself)-(point 1 of lineItself)) rotated 90; + lineBleeding := + ((point 0 of lineItself) shifted (unitvector((point 0 of lineItself) - (point 1 of lineItself)) scaled 2dpLength[dp])) -- + lineItself -- ((point 1 of lineItself) shifted (unitvector((point 1 of lineItself) - (point 0 of lineItself)) scaled 2dpLength[dp])); + lineStyShift := (linePerp scaled (1/2currentLineWidth*s)); + p := image( + pickup pencircle scaled currentLineWidth; + if (dp > 0): + draw (lineItself shifted lineStyShift) withcolor white; + draw (lineItself shifted lineStyShift) withcolor col + dashed byDashPattern(arclength(lineItself), dp); + draw (subpath(0, 1) of lineBleeding shifted lineStyShift) withcolor col; + draw (subpath(2, 3) of lineBleeding shifted lineStyShift) withcolor col; + else: + draw (lineBleeding shifted lineStyShift) withcolor col; + fi; + if (th < 0): + draw (lineBleeding shifted (lineStyShift + (linePerp scaled 1/4currentLineWidth))) withpen pencircle scaled 1/2currentLineWidth withcolor black; + fi; + ); + clippingPathPoint1 + = whatever[aS shifted (linePerp*1/2lineWidth) shifted lineStyShift, + bS shifted (linePerp*1/2lineWidth) shifted lineStyShift] + = whatever[aS, aS shifted cutAngle1]; + clippingPathPoint2 + = whatever[aS shifted (linePerp*-1/2lineWidth) shifted lineStyShift, + bS shifted (linePerp*-1/2lineWidth) shifted lineStyShift] + = whatever[aS, aS shifted cutAngle1]; + clippingPathPoint3 + = whatever[aS shifted (linePerp*-1/2lineWidth) shifted lineStyShift, + bS shifted (linePerp*-1/2lineWidth) shifted lineStyShift] + = whatever[bS, bS shifted cutAngle2]; + clippingPathPoint4 + = whatever[aS shifted (linePerp*1/2lineWidth) shifted lineStyShift, + bS shifted (linePerp*1/2lineWidth) shifted lineStyShift] + = whatever[bS, bS shifted cutAngle2]; + clippingPath := clippingPathPoint1 -- clippingPathPoint2 -- clippingPathPoint3 -- clippingPathPoint4 -- cycle; + clip p to clippingPath; + if mainPictureMode and ghostLines: + save gP; + picture gP; + gP := ghostPicture; + ghostPicture := image( + draw gP; + begingroup; + save ghostLines; + boolean ghostLines; + ghostLines := false; + draw byLineRender (a, b, white, dp, 2, c, d, ct, dt, s, sf); + % draw byLineRender (a, b, col, dp, 2, c, d, ct, dt, s, sf); + endgroup; + ); + fi; + p rotated globalRotation +enddef; + +vardef byReturnLineLength (suffix a, b) = + if byIsPointInSpace(a, b): + absXYZ(pointXYZ.a - pointXYZ.b) + else: + abs(a-b) + fi +enddef; + +vardef byLineDefine@#(suffix a, b)(expr col, dp, th) = +if str @# = "": + byLineDefine.scantokens(str a & str b)(a, b)(col, dp, th); +else: + save lA; + if mainPictureMode: + appendList(allLinesList, str @#, 1, true); + fi; + if byIsPointInSpace(a, b): + if (typeOf(a) = "pair") and (typeOf(b) = "pair"): + if (a <> b): + lA := angle(b-a); + else: + lA := 0; + fi; + else: + lA := 0; + fi; + else: + if (a <> b): + lA := angle(b-a); + else: + lA := 0; + fi; + fi; + setAttribute("line", "Angle", str @#, lA); + setAttribute("line", "Color", str @#, col); + setAttribute("line", "Dashed", str @#, dp); + setAttribute("line", "Thin", str @#, th); + setAttribute("line", "EndA", str @#, a); + setAttribute("line", "EndB", str @#, b); + setAttribute("line", "Shift", str @#, 0); + setAttribute("line", "DirA", str @#, a); + setAttribute("line", "DirB", str @#, b); + setAttribute("line", "EndAType", str @#, 0); + setAttribute("line", "EndBType", str @#, 0); + setAttribute("line", "EndAName", str @#, str a); + setAttribute("line", "EndBName", str @#, str b); + setAttribute("line", "DirAName", str @#, str a); + setAttribute("line", "DirBName", str @#, str b); + setAttribute("line", "UseLineLabel", str @#, false); + setAttribute("line", "Label", str @#, str @#); + setAttribute("line", "Synonym", str @#, str @#); +fi; +enddef; + +vardef byLineStylize (suffix c, d)(expr ct, dt, s) (suffix lineName) = + setAttribute("line", "Shift", str lineName, s); + setAttribute("line", "DirA", str lineName, c); + setAttribute("line", "DirB", str lineName, d); + setAttribute("line", "DirAName", str lineName, str c); + setAttribute("line", "DirBName", str lineName, str d); + setAttribute("line", "EndAType", str lineName, ct); + setAttribute("line", "EndBType", str lineName, dt); +enddef; + +vardef byLine@#(suffix a, b)(expr col, dp, th) = +if str @# = "": + byLine.scantokens(str a & str b)(a, b)(col, dp, th) +else: + byLineDefine@#(a, b, col, dp, th); + byNamedLineFull(a, b, 0, 0, 0)(@#) +fi +enddef; + +vardef byLineFull@#(suffix a, b)(expr col, dp, th)(suffix c, d)(expr ct, dt, s) = +if str @# = "": + byLineFull.scantokens(str a & str b)(a, b)(col, dp, th)(c, d)(ct, dt, s) +else: + byLineDefine@#(a, b) (col, dp, th); + byLineStylize (c, d, ct, dt, s)(@#); + byNamedLine(@#) +fi +enddef; + +% This one's behaviour is slightly off, since it affects line label type + +vardef byLineWithName (suffix a, b)(expr col, dp, th)(suffix lineLabel) = + byLineDefine.lineLabel(a, b, col, dp, th); + setAttribute("line", "UseLineLabel", str lineLabel, true); + byNamedLineFull(a, b, 0, 0, 0)(lineLabel) +enddef; + +vardef byNamedLineFull (expr c, d, ct, dt, s) (suffix lineName) = + if (mainPictureMode): + appendList(pointLinesList.scantokens(getAttribute("line", "EndAName", str lineName)), str lineName, 1, true); + appendList(pointLinesList.scantokens(getAttribute("line", "EndBName", str lineName)), str lineName, 1, true); + fi; + byLineRender( + scantokens(getAttribute("line", "EndAName", str lineName)), + scantokens(getAttribute("line", "EndBName", str lineName)), + getAttribute("line", "Color", str lineName), + getAttribute("line", "Dashed", str lineName), + getAttribute("line", "Thin", str lineName), + c, d, ct, dt, s, scaleFactor) +enddef; + +vardef byNamedLine (text linesList) = + image( + forsuffixes lN=linesList: + draw byNamedLineFull( + scantokens(getAttribute("line", "DirAName", str lN)), + scantokens(getAttribute("line", "DirBName", str lN)), + getAttribute("line", "EndAType", str lN), + getAttribute("line", "EndBType", str lN), + getAttribute("line", "Shift", str lN))(lN); + endfor; + ) +enddef; + +vardef byNamedLineSeq (expr s)(text linesListRaw) = + save lAname, lBname, lShift, lName, i, j, k, l, m, n, c, d, prp, pointNames, linesList; + string lAname[], lBname[], lName[], c, d, pointNames, linesList; + numeric i, j, k, l, m, n, prp, lShift[]; + linesList := byExpandLines(linesListRaw); + pointNames := ""; + image( + i := -1; + forsuffixes lN=scantokens(linesList): + i := i + 1; + lAname[i] := getAttribute("line", "EndAName", str lN); + lBname[i] := getAttribute("line", "EndBName", str lN); + lShift[i] := getAttribute("line", "Shift", str lN); + lName[i] := str lN; + endfor; + if (i = 1): + i := i + 1; + lAname[i] := "noLine"; + lBname[i] := "noLine"; + lName[i] := "noLine"; + fi; + if (i > 0): + for j := 0 step 1 until i: + k := cycleval (j - 1, i + 1); + l := cycleval (j + 1, i + 1); + m := 0; + n := 0; + if (lAname[k] = "noLine"): + k := cycleval (k - 1, i + 1); + m := 1; + fi; + if (lAname[l] = "noLine"): + l := cycleval (l + 1, i + 1); + n := 1; + fi; + c := lAname[j]; + d := lBname[j]; + prp := 0; + if (((lAname[j] = lAname[k]) or (lAname[j] = lBname[k])) + and (not ((m <> 0) and (lName[k] = lName[l])))) or + (((lBname[j] = lAname[l]) or (lBname[j] = lBname[l])) + and (not ((n <> 0) and (lName[k] = lName[l])))): + if (lAname[j] = lAname[k]): + if (m = 0): c := lBname[k]; fi; + elseif (lAname[j] = lBname[k]): + if (m = 0): c := lAname[k]; fi; + else: + m := 2; + fi; + if (lBname[j] = lAname[l]): + if (n = 0): d := lBname[l]; fi; + elseif (lBname[j] = lBname[l]): + if (n = 0): d := lAname[l]; fi; + else: + n := 2; + fi; + prp := 1; + else: + if (lAname[j] = lAname[l]): + if (n = 0): c := lBname[l]; fi; + elseif (lAname[j] = lBname[l]): + if (n = 0): c := lAname[l]; fi; + else: + n := 2; + fi; + if (lBname[j] = lAname[k]): + if (m = 0): d := lBname[k]; fi; + elseif (lBname[j] = lBname[k]): + if (m = 0): d := lAname[k]; fi; + else: + m := 2; + fi; + prp := -1; + fi; + if (lName[j] <> "noLine"): + if (prp = 1): + pointNames := pointNames & lAname[j] & ", "; + if (n = 2): + pointNames := pointNames & lBname[j] & ", noPoint, "; + fi; + elseif (prp = -1): + pointNames := pointNames & lBname[j] & ", "; + if (n = 2): + pointNames := pointNames & lAname[j] & ", noPoint, "; + fi; + fi; + lShift[j] := s*prp; + draw byNamedLineFull(scantokens(c), scantokens(d), 0, 0, -s*sign(prp))(scantokens(lName[j])); + else: + prp := -2; + fi; + endfor; + if (prp = 1) and (n <> 2): + pointNames := pointNames & lBname[i]; + elseif (prp = -1) and (n <> 2): + pointNames := pointNames & lAname[i]; + else: + pointNames := substring (0, length(pointNames)-2) of pointNames; + fi; + if (textLabels and autoLabelingMode): + draw byLabelsOnPolygon(scantokens pointNames)(1, s); + fi; + else: + forsuffixes lN=scantokens(linesList): + setAttribute("line", "Shift", str lN, s); + draw byNamedLineFull( + scantokens(getAttribute("line", "EndAName", str lN)), + scantokens(getAttribute("line", "EndBName", str lN)), + 0, 0, -s)(lN); + if (textLabels and autoLabelingMode): + draw byLabelsOnPolygon(scantokens (getAttribute("line", "EndAName", str lN) & ", " & getAttribute("line", "EndBName", str lN)))(1, s); + fi; + endfor; + fi; + ) +enddef; + +vardef byMarkLine(expr pos, col)(suffix lineName) = + setAttribute("mark", "Position", str lineName, pos); + setAttribute("mark", "Color", str lineName, col); + byNamedMarkLine(lineName) +enddef; + +vardef byNamedMarkLine(suffix lineName) = + save p, q; + pair q[]; + picture p; + p := image( + q0 := getAttribute("mark", "Position", str lineName)[ + scantokens(getAttribute("line", "EndAName", str lineName)), + scantokens(getAttribute("line", "EndBName", str lineName)) + ]; + q1 := q0 shifted (dir(getAttribute("line", "Angle", str lineName) + 90) scaled 1/2markLength); + q2 := q0 shifted (dir(getAttribute("line", "Angle", str lineName) - 90) scaled 1/2markLength); + q3 := (dir(getAttribute("line", "Angle", str lineName) - 90) scaled (1/2lineWidth*getAttribute("line", "Shift", str lineName))); + draw (q1--q2) shifted q3 withpen pencircle scaled lineWidthThin withcolor getAttribute("mark", "Color", str lineName); + ); + p rotated globalRotation +enddef; + +vardef byFindLinePointedThere (expr a, b) = + save endA, endB, endAC, endBC, endAA, endBA, chosenLine, lL; + string chosenLine; + pair endA, endB, endAC, endBC, endAA, endBA; + numeric lL; + endA := scantokens(a); + endB := b; + if known pointLinesList.scantokens(a): + forsuffixes lL=scantokens(pointLinesList.scantokens(a)): + endAC := scantokens(getAttribute("line", "EndAName", str lL)); + endBC := scantokens(getAttribute("line", "EndBName", str lL)); + if (endAC = endA): + endAA := endAC; + endBA := endBC; + elseif (endBC = endA): + endAA := endBC; + endBA := endAC; + fi; + if ((abs(angle(endBA - endAA) - angle(endB - endA)) mod 360) < 1): + chosenLine := str lL; + fi; + endfor; + fi; + if unknown chosenLine: + chosenLine := ""; + fi; + chosenLine +enddef; + +vardef byConstructCompoundLine (expr lineName) = + save lineStart, lEnd, newLine, lEndA, lEndB, compoundLineString, i, j, cPointA, cPointB; + string lineStart, lEnd, newLine, lEndA, lEndB, compoundLineString, cPointA, cPointB; + numeric i, j; + j := 0; + if (length(lineName) = 2): + lineStart := substring (0, 1) of lineName; + lEnd := substring (1, 2) of lineName; + else: + for i := 1 step 1 until length(lineName): + cPointA := substring (0, i) of lineName; + cPointB := substring (i, length(lineName)) of lineName; + if known scantokens(cPointA): % for some reason 'and' doesn't work here + if known scantokens(cPointB): + j := j + 1; + lineStart := cPointA; + lEnd := cPointB; + fi; + fi; + endfor; + fi; + if unknown lineStart: + errmessage("Can't find points for line: " & lineName); + fi; + if j > 1: + errmessage("Ambiguous line name: " & lineName); + fi; + i := 0; + forever: + i := i + 1; + newLine := byFindLinePointedThere(lineStart, scantokens(lEnd)); + if (newLine = ""): + errmessage("There's no line from " & lineStart & " to " & lEnd); + fi; + appendList (compoundLineString, newLine, 1, false); + lEndA := getAttribute("line", "EndAName", newLine); + lEndB := getAttribute("line", "EndBName", newLine); + if (lEndA = lineStart): + lineStart := lEndB; + elseif (lEndB = lineStart): + lineStart := lEndA; + fi; + exitif (i > 10); + exitif (lEndA = lEnd); + exitif (lEndB = lEnd); + endfor; + compoundLineString +enddef; + +vardef byExpandLines (text linesList) = + save lineString, compoundLineString; + string lineString, compoundLineString; + forsuffixes lN=linesList: + if attributeExists("line", "Synonym", str lN): + appendList(lineString, str lN, 1, false); + else: + appendList(lineString, byConstructCompoundLine(str lN), 1, false); + fi; + endfor; + lineString +enddef; + +vardef byNamedCompoundLine (expr cu, pw)(text linesList) = + byNamedCompoundLineRender(cu, pw, 0)(scantokens(byExpandLines(linesList))) +enddef; + +vardef byNamedCompoundRay (expr cu, pw)(text linesList) = + byNamedCompoundLineRender(cu, pw, 1)(scantokens(byExpandLines(linesList))) +enddef; + +vardef byNamedCompoundIndLine (expr cu, pw)(text linesList) = + byNamedCompoundLineRender(cu, pw, 2)(scantokens(byExpandLines(linesList))) +enddef; + +vardef byNamedCompoundLineRender (expr cu, pw, sty)(text linesList) = + save correctedLength, currentPosition, a, b, sc, totalLength, pwL, lineNames, endNames, i, j, angleCheck, lineNotStraight, lineNotContinuous, globalRotation, extensionNames, textLabelImage; + numeric correctedLength, currentPosition[], sc, totalLength, pwL, i, j, angleCheck[]; + globalRotation := 0; + string lineNames[], endNames[], extensionNames[]; + pair a, b; + picture textLabelImage[]; + boolean lineNotStraight, lineNotContinuous; + i := 0; + totalLength := 0; + lineNotStraight := false; + lineNotContinuous := false; + forsuffixes lN=linesList: + lineNames[i] := getAttribute("line", "EndAName", str lN); + lineNames[i+1] := getAttribute("line", "EndBName", str lN); + if (i > 0): + if (lineNames[i] <> lineNames[i-2]) + and (lineNames[i] <> lineNames[i-1]) + and (lineNames[i+1] <> lineNames[i-2]) + and (lineNames[i+1] <> lineNames[i-1]): + lineNotContinuous := true; + fi; + fi; + i := i + 2; + angleCheck[i/2] := abs(cosd(getAttribute("line", "Angle", str lN))); + if (abs(angleCheck[i/2] - angleCheck[1]) > 1/100): + lineNotStraight := true; + fi; + totalLength := totalLength + + byReturnLineLength( + scantokens(getAttribute("line", "EndAName", str lN)), + scantokens(getAttribute("line", "EndBName", str lN))); + endfor; + if (lineNotStraight): + message("The line is not straight"); + fi; + if (lineNotContinuous): + errmessage("The line is not continuous"); + fi; + if (pw <> 0): + sc := cu**(1 - (1/pw)); + pwL := 1/pw; + else: + sc := cu/(totalLength*scaleFactor); + pwL := 1; + fi; + currentPosition0 := 0; + image( + j := 0; + forsuffixes lN=linesList: + correctedLength := (byReturnLineLength( + scantokens(getAttribute("line", "EndAName", str lN)), + scantokens(getAttribute("line", "EndBName", str lN)) + )**pwL)*sc; + currentPosition1 := currentPosition0 + correctedLength; + a := (currentPosition0, 0); + b := (currentPosition1, 0); + draw byLineRender( + a, b, + getAttribute("line", "Color", str lN), + getAttribute("line", "Dashed", str lN), + getAttribute("line", "Thin", str lN), + a, b, 0, 0, 0, scaleFactor); + currentPosition0 := currentPosition1; + if (i > 2): + if (j = 0): + extensionNames3 := str lN; + if (getAttribute("line", "EndAName", str lN) = lineNames[2]) + or (getAttribute("line", "EndAName", str lN) = lineNames[3]): + endNames[0] := getAttribute("line", "EndBName", str lN); + elseif (getAttribute("line", "EndBName", str lN) = lineNames[2]) + or (getAttribute("line", "EndBName", str lN) = lineNames[3]): + endNames[0] := getAttribute("line", "EndAName", str lN); + fi; + fi; + if (j = (i-2)/2): + extensionNames2 := str lN; + if (getAttribute("line", "EndAName", str lN) = lineNames[i-3]) + or (getAttribute("line", "EndAName", str lN) = lineNames[i-4]): + endNames[1] := getAttribute("line", "EndBName", str lN); + elseif (getAttribute("line", "EndBName", str lN) = lineNames[i-3]) + or (getAttribute("line", "EndBName", str lN) = lineNames[i-4]): + endNames[1] := getAttribute("line", "EndAName", str lN); + fi; + fi; + else: + endNames[0] := getAttribute("line", "EndAName", str lN); + endNames[1] := getAttribute("line", "EndBName", str lN); + if (getAttribute("line", "UseLineLabel", str lN)): + endNames[2] := str lN; + fi; + fi; + j := j + 1; + endfor; + extensionNames0 := byFindLinePointedThere(endNames1, 2[scantokens(endNames0), scantokens(endNames1)]); + extensionNames1 := byFindLinePointedThere(endNames0, 2[scantokens(endNames1), scantokens(endNames0)]); + if extensionNames0 = "": + extensionNames0 := "dummyLine"; + else: + extensionNames2 := extensionNames0; + fi; + if extensionNames1 = "": + extensionNames1 := "dummyLine"; + else: + extensionNames3 := extensionNames1; + fi; + if (sty > 0): + draw byLineRender(b, b shifted (rayExtension/scaleFactor, 0), + getAttribute("line", "Color", extensionNames0), + getAttribute("line", "Dashed", extensionNames0), + getAttribute("line", "Thin", extensionNames2), + b, b shifted (rayExtension/scaleFactor, 0), 0, 0, 0, scaleFactor); + fi; + if (sty > 1): + draw byLineRender((0, 0), (0, 0) shifted (-rayExtension/scaleFactor, 0), + getAttribute("line", "Color", extensionNames1), + getAttribute("line", "Dashed", extensionNames1), + getAttribute("line", "Thin", extensionNames3), + (0, 0), (0, 0) shifted (-rayExtension/scaleFactor, 0), 0, 0, 0, scaleFactor); + fi; + if (textLabels and autoLabelingMode): + if unknown endNames[2]: + textLabelImage1 := byTextLabel(pointLabel)(endNames[0], (0, 0), 90, textLabelShift); + textLabelImage2 := byTextLabel(pointLabel)(endNames[1], b, 90, textLabelShift); + draw textLabelImage1; + draw textLabelImage2; + % these are only needed to make the image symmetrical in order to have + % the line with and without labels at the same level in text. + draw (textLabelImage1 yscaled -1) withcolor white; + draw (textLabelImage2 yscaled -1) withcolor white; + else: + textLabelImage1 := byTextLabel(lineLabel)(endNames[2], 1/2[(0, 0), b], 90, textLabelShift); + draw textLabelImage1; + draw (textLabelImage1 yscaled -1) withcolor white; + fi; + fi; + ) +enddef; + +vardef generateLineSynonyms = + if string allLinesList: + save candidateName, originalName; + string candidateName[], originalName; + forsuffixes i=scantokens(allLinesList): + originalName := str i; + candidateName1 := getAttribute("line", "EndAName", originalName) + & getAttribute("line", "EndBName", originalName); + candidateName2 := getAttribute("line", "EndBName", originalName) + & getAttribute("line", "EndAName", originalName); + if not string getAttribute("line", "Synonym", candidateName1): + setAttribute("line", "Synonym", candidateName1, originalName); + fi; + if not string getAttribute("line", "Synonym", candidateName2): + setAttribute("line", "SynonymPartial", candidateName2, true); + setAttribute("line", "Synonym", candidateName2, originalName); + setAttribute("line", "EndA", candidateName2, getAttribute("line", "EndB", originalName)); + setAttribute("line", "EndB", candidateName2, getAttribute("line", "EndA", originalName)); + setAttribute("line", "EndAType", candidateName2, getAttribute("line", "EndBType", originalName)); + setAttribute("line", "EndBType", candidateName2, getAttribute("line", "EndAType", originalName)); + setAttribute("line", "EndAName", candidateName2, getAttribute("line", "EndBName", originalName)); + setAttribute("line", "EndBName", candidateName2, getAttribute("line", "EndAName", originalName)); + setAttribute("line", "DirA", candidateName2, getAttribute("line", "DirB", originalName)); + setAttribute("line", "DirB", candidateName2, getAttribute("line", "DirA", originalName)); + setAttribute("line", "DirAName", candidateName2, getAttribute("line", "DirBName", originalName)); + setAttribute("line", "DirBName", candidateName2, getAttribute("line", "DirAName", originalName)); + setAttribute("line", "Angle", candidateName2, 180 + getAttribute("line", "Angle", originalName)); + fi; + endfor; + fi; +enddef; + +% +% Points +% + +vardef byPointLabelDefine (suffix pointName)(expr t) = + setAttribute("point", "Label", str pointName, t); +enddef; + +vardef byPointLabelRemove (text pointsList) = + forsuffixes i=pointsList: + byPointLabelDefine(i, ""); + endfor; +enddef; + +vardef byPointMarkDefine (suffix pointName)(expr col, sty) = + setAttribute("point", "Color", str pointName, col); + setAttribute("point", "Style", str pointName, sty); +enddef; + +vardef byPointXYZDefine@#(expr ox, oy, oz) = +if str @# = "": + errmessage("byPointXYZDefine needs a name (byPointXYZDefine.somename...)"); +else: + if mainPictureMode: + appendList(allPointsList, str @#, 1, true); + fi; + setAttribute("point", "XYZ", str @#, (ox, oy, oz)); + pair @#; + color pointXYZ.@#; + @# := (ox, oy); + pointXYZ.@# := (ox, oy, oz); +fi; +enddef; + +vardef byPointXYZEmpty (text pointNames) = + forsuffixes pN = pointNames: + if mainPictureMode: + appendList(allPointsList, str pN, 1, true); + fi; + color pointXYZ.pN; + endfor; +enddef; + +vardef byIsPointInSpace (text pointNames) = + save pN; + (true forsuffixes pN = pointNames: + and (typeOf(pointXYZ.pN) = "color") + endfor) +enddef; + +% in order to automatically project points they need to be added +% to allPointsList list. +vardef byPair (text pointsList) = + save i; + pair pointsList; + forsuffixes i := pointsList: + appendList(allPointsList, str i, 1, true); + numeric pointXYZ.i; + endfor; +enddef; + +% @# here stands for rotation order, e. g. byRotatePoints.XYZ + +vardef byRotatePoints@#(expr xa, ya, za, toRotate)(text pointsList) = + save oxyz, pointsToProcess; + color oxyz; + string pointsToProcess; + forsuffixes i=pointsList: + appendList(pointsToProcess, str i, 1, true); + endfor; + if (length(pointsToProcess) = 0): + pointsToProcess := allPointsList; + fi; + forsuffixes i=scantokens(pointsToProcess): + if (known i) or (known pointXYZ.i): + if attributeExists("point", "XYZ", str i): + oxyz := getAttribute("point", "XYZ", str i); + elseif typeOf(i) = "pair": + byPointXYZDefine.i(xpart i, ypart i, 0); + oxyz := getAttribute("point", "XYZ", str i); + else: + errmessage("Point " & str i & " doesn't exist"); + fi; + oxyz := rotateColor@#(oxyz, (xa, ya, za)); + pair i; + i := (redpart(oxyz), greenpart(oxyz)); + if toRotate: + setAttribute("point", "XYZ", str i, oxyz); + fi; + fi; + endfor; +enddef; + +vardef byPointMarkRender (expr p, col, sty) = + image( + if (sty = 0): + fill ((fullcircle scaled pointMarkSize) shifted (p scaled scaleFactor)) withcolor col; + elseif (sty = 1): + fill ((fullcircle scaled pointMarkSize) shifted (p scaled scaleFactor)) withcolor white; + draw ((fullcircle scaled pointMarkSize) shifted (p scaled scaleFactor)) withcolor col withpen pencircle scaled lineWidthHair; + fi; + ) +enddef; + +vardef byNamedPointMark (text pointsList) = + image( + forsuffixes pN=pointsList: + if (attributeExists("point", "Color", str pN)): + appendList(pL, str pN, 1, false); + draw byPointMarkRender(pN, getAttribute("point", "Color", str pN), getAttribute("point", "Style", str pN)) rotated globalRotation; + if (autoLabelingMode and textLabels): + draw byTextLabel(pointLabel)(str pN, pN, 90, 1/2pointMarkSize + textLabelShift); + fi; + fi; + endfor; + ) +enddef; + +vardef byPointMark (suffix pointName)(expr col, sty) = + image( + byPointMarkDefine(pointName)(col, sty); + draw byNamedPointMark(pointName); + ) +enddef; + +vardef byNamedPointLines (suffix pointName)(expr omitLines) = +save linesList, lineNames, pointNames, distantEnd, distantEnds, n, no, distL, maxDist, maxDistEndA, maxDistEndB, a, angleL, minAngle, maxAngle, minAngleEndB, maxAngleEndA, maxAngleEndB, safeLine; +string linesList, lineNames[], pointNames[]; +pair distantEnd, distantEnds[]; +numeric n, no, distL, maxDist, maxDistEndA, maxDistEndB, a[], angleL, minAngle, maxAngle, minAngleEndB, maxAngleEndA, maxAngleEndB, safeLine; +picture labelImage, linesImage; +boolean omitLine[]; +if known pointLinesList.pointName: + linesList := pointLinesList.pointName; + n := 0; + no := 0; + maxDist := 0; + maxAngle := 0; + minAngle := 360; + forsuffixes i = scantokens(linesList): + n := n + 1; + lineNames[n] := str i; + omitLine[n] := false; + if (length(omitLines) > 0): + forsuffixes j = scantokens(omitLines): + if ((str i) = (str j)): + omitLine[n] := true; + no := no + 1; + fi; + endfor; + fi; + if (not omitLine[n]): + safeLine := n; + fi; + if (getAttribute("line", "EndAName", str i) = str pointName): + distantEnds[n] := + scantokens(getAttribute("line", "EndAName", str i)) + + (unitvector(scantokens(getAttribute("line", "EndBName", str i)) + - scantokens(getAttribute("line", "EndAName", str i))) scaled (pointLinesSize/scaleFactor) + ); + pointNames[n] := getAttribute("line", "EndBName", str i); + else: + distantEnds[n] := + scantokens(getAttribute("line", "EndBName", str i)) + + (unitvector(scantokens(getAttribute("line", "EndAName", str i)) + - scantokens(getAttribute("line", "EndBName", str i))) scaled (pointLinesSize/scaleFactor) + ); + pointNames[n] := getAttribute("line", "EndAName", str i); + fi; + endfor; + if (n - no > 1): + for i := 1 step 1 until n: + if (not omitLine[i]): + minAngle := 360; + for j := 1 step 1 until n: + if (not omitLine[j]): + distL := abs(distantEnds[i] - distantEnds[j]); + if (distL > maxDist): + maxDist := distL; + maxDistEndA := i; + maxDistEndB := j; + fi; + a1 := angle(distantEnds[i] - pointName); + a2 := angle(distantEnds[j] - pointName); + angleL := a2 - a1; + if (angleL < 0): angleL := angleL + 360; fi; + if (minAngle > angleL) and (angleL <> 0): + minAngle := angleL; + minAngleEndA := j; + fi; + fi; + endfor; + if (maxAngle <= minAngle + 1): + maxAngle := minAngle; + maxAngleEndA := minAngleEndA; + maxAngleEndB := i; + fi; + fi; + endfor; + linesImage := image( + for i := 1 step 1 until n: + if (i <> maxDistEndA) and (i <> maxDistEndB) and (not omitLine[i]): + draw byLineRender(pointName, distantEnds[i], + getAttribute("line", "Color", lineNames[i]), getAttribute("line", "Dashed", lineNames[i]), getAttribute("line", "Thin", lineNames[i]), + pointName, distantEnds[i], 0, 0, 0, scaleFactor); + fi; + endfor; + draw byLineRender(pointName, distantEnds[maxDistEndA], + getAttribute("line", "Color", lineNames[maxDistEndA]), + getAttribute("line", "Dashed", lineNames[maxDistEndA]), + getAttribute("line", "Thin", lineNames[maxDistEndA]), + distantEnds[maxDistEndB], distantEnds[maxDistEndA], 0, 0, 0, scaleFactor); + draw byLineRender(pointName, distantEnds[maxDistEndB], + getAttribute("line", "Color", lineNames[maxDistEndB]), + getAttribute("line", "Dashed", lineNames[maxDistEndB]), + getAttribute("line", "Thin", lineNames[maxDistEndB]), + distantEnds[maxDistEndA], distantEnds[maxDistEndB], 0, 0, 0, scaleFactor); + ); + labelImage := image( + if (textLabels): + draw byLabelsOnPolygon(scantokens(pointNames[maxAngleEndA]),pointName,scantokens(pointNames[maxAngleEndB]))(2, 0); + fi; + ); + else: + linesImage := image( + draw byLineRender(pointName, distantEnds[safeLine], + getAttribute("line", "Color", lineNames[safeLine]), + getAttribute("line", "Dashed", lineNames[safeLine]), + getAttribute("line", "Thin", lineNames[safeLine]), + pointName, distantEnds[safeLine], 0, 0, 0, scaleFactor); + ); + labelImage := image( + if (textLabels): + draw byLabelLineEnd (pointName, distantEnds[safeLine])(0); + fi; + ); + fi; +else: + labelImage := image(); + linesImage := image(); +fi; + image( + draw labelImage; + % this thing is intended to make the overall picture symmetrical + % when all the lines are horizontal. + draw labelImage rotatedabout(pointName scaled scaleFactor, 180) withcolor white; + draw linesImage; + ) +enddef; + +% +% Arcs and circles +% + +vardef byCirclePathGenerate (expr sr, pv) = + save circPath, xp, xang, yang; + path circPath; + color pvt; + numeric xang, yang; + pair xp; + circPath := fullcircle; + xp := (bluepart(pv), greenpart(pv)); + xang := angle(xp); + pvt := (redpart(pv), ypart(xp rotated -xang), xpart(xp rotated -xang)); + yang := angle((bluepart(pvt), redpart(pvt))); + circPath := spaceRotated(circPath)((-xang, yang, 0)); % plane vector to angles conversion should eventually go somewhere + circPath := spaceRotated(circPath)(sr); + circPath := spaceRotated(circPath)(projectionAngle); + circPath +enddef; + +vardef byArcRender (expr o, b, e, r, sr, pv, col, dp, th, s, et) = + save p, q, m, t, currentLineWidth, srCircle; + path p[], srCircle; + picture q; + pair t[]; + numeric m[]; + numeric currentLineWidth; + save thirdDimension; + image( + srCircle := byCirclePathGenerate(sr, pv); + currentLineWidth := lineThickness(th); + p0 := ((subpath (b, e) of srCircle) scaled (r*scaleFactor + lineWidth*s + lineWidth)) shifted (o*scaleFactor); + p1 := ((subpath (b, e) of srCircle) scaled (r*scaleFactor)) shifted (o*scaleFactor); + p2 := (srCircle scaled (r*scaleFactor + lineWidth*s + lineWidth)) shifted (o*scaleFactor); + p3 := ((subpath (b, e) of srCircle) scaled (r*scaleFactor + lineWidth*s)) shifted (o*scaleFactor); + p4 := ((subpath (b + sign(b-e)*1/16, b) of srCircle) scaled (r*scaleFactor + lineWidth*s)) shifted (o*scaleFactor); + p5 := ((subpath (e, e + sign(e-b)*1/16) of srCircle) scaled (r*scaleFactor + lineWidth*s)) shifted (o*scaleFactor); + q := image( + draw p3 withpen pencircle scaled currentLineWidth withcolor col if (dp > 0): dashed byDashPattern(arclength(p3), dp) fi; + draw p4 withpen pencircle scaled currentLineWidth withcolor col; + draw p5 withpen pencircle scaled currentLineWidth withcolor col; + ); + if mainPictureMode and ghostLines: + save gP; + picture gP; + gP := ghostPicture; + ghostPicture := image( + draw gP; + draw p3 withcolor white withpen pencircle scaled lineWidthHair; + ); + fi; + if (et = 0): + p6 := p0--(o*scaleFactor)--cycle; + else: + t0 := point 0 of p1; + t1 := point 0 of reverse(p1); + t2 := 1/2[t0, t1]; + m0 := xpart (p2 intersectiontimes (t2 -- 2[t2, t0])); + m1 := xpart (p2 intersectiontimes (t2 -- 2[t2, t1])); + for i := -(8*3) step 8 until (8*3): + if (abs(b - m0) > abs(b - (m0 + i))): + m0 := m0 + i; + fi; + if (abs(e - m1) > abs(e - (m1 + i))): + m1 := m1 + i; + fi; + endfor; + p6 := (subpath(m0, m1) of p2) -- cycle; + fi; + clip q to p6; + draw q; + ) rotated globalRotation +enddef; + +vardef byArcDefineBE@#(suffix o)(expr b, e, r, col, dp, th, s, et) = +if str @# = "": + byArcDefineBE.scantokens(str o)(b, e, r, col, dp, th, s, et); +else: + setAttribute("arc", "Color", str @#, col); + setAttribute("arc", "Dashed", str @#, dp); + setAttribute("arc", "Thin", str @#, th); + setAttribute("arc", "Shift", str @#, s); + setAttribute("arc", "Diameter", str @#, 2r); + setAttribute("arc", "Center", str @#, o); + setAttribute("arc", "CenterName", str @#, str o); + setAttribute("arc", "BName", str @#, ""); + setAttribute("arc", "EName", str @#, ""); + setAttribute("arc", "Begin", str @#, b); + setAttribute("arc", "End", str @#, e); + setAttribute("arc", "EndType", str @#, et); + setAttribute("arc", "Angle", str @#, angle((point b of fullcircle) - (point e of fullcircle))); + setAttribute("arc", "SpaceRotation", str @#, (0,0,0)); + setAttribute("arc", "PlaneVector", str @#, (0,0,1)); + setAttribute("arc", "Synonym", str @#, str @#); +fi; +enddef; + +vardef byArcBE@#(suffix o)(expr b, e, r, col, dp, th, s, et) = +if str @# = "": + byArcBE.scantokens(str o)(o)(b, e, r, col, dp, th, s, et) +else: + byArcDefineBE.@#(o, b, e, r, col, dp, th, s, et); + byNamedArcExact(@#) +fi +enddef; + +vardef byArcDefine@#(suffix o, pb, pe)(expr r, col, dp, th, s, et) = +if str @# = "": + byArcDefine.scantokens(str pb & str o & str pe)(o, pb, pe)(r, col, dp, th, s, et); +else: + save b, e; + numeric b, e; + b := xpart(((fullcircle scaled 2r) shifted o) intersectiontimes (o -- 2[o, pb])); + e := xpart(((fullcircle scaled 2r) shifted o) intersectiontimes (o -- 2[o, pe])); + if (b > e): + b := b - 8; + fi; + byArcDefineBE.@#(o, b, e, r, col, dp, th, s, et); + setAttribute("arc", "BName", str @#, str pb); + setAttribute("arc", "EName", str @#, str pe); +fi; +enddef; + +vardef byArc@#(suffix o, pb, pe)(expr r, col, dp, th, s, et) = +if str @# = "": + byArc.scantokens(str pb & str o & str pe)(o, pb, pe)(r, col, dp, th, s, et) +else: + byArcDefine.@#(o, pb, pe, r, col, dp, th, s, et); + byNamedArcExact(@#) +fi +enddef; + +vardef byNamedArcExact (text arcslist) = + image( + forsuffixes aN=arcslist: + draw byArcRender( + scantokens(getAttribute("arc", "CenterName", str aN)), + getAttribute("arc", "Begin", str aN), + getAttribute("arc", "End", str aN), + getAttribute("arc", "Diameter", str aN), + getAttribute("arc", "SpaceRotation", str aN), + getAttribute("arc", "PlaneVector", str aN), + getAttribute("arc", "Color", str aN), + getAttribute("arc", "Dashed", str aN), + getAttribute("arc", "Thin", str aN), + getAttribute("arc", "Shift", str aN), + getAttribute("arc", "EndType", str aN)); + endfor; + if (autoLabelingMode): + draw byNamedArcLabel(arcslist); + fi; + ) +enddef; + +vardef byNamedArc (text arcslist) = + image( + forsuffixes aN=arcslist: + draw byArcRender( + scantokens(getAttribute("arc", "CenterName", str aN)), + getAttribute("arc", "Begin", str aN), + getAttribute("arc", "End", str aN), + getAttribute("arc", "Diameter", str aN), + getAttribute("arc", "SpaceRotation", str aN), + getAttribute("arc", "PlaneVector", str aN), + getAttribute("arc", "Color", str aN), + getAttribute("arc", "Dashed", str aN), + getAttribute("arc", "Thin", str aN), 0, 0); + endfor; + if (autoLabelingMode): + draw byNamedArcLabel(arcslist); + fi; + ) +enddef; + +vardef byNamedArcLabel (text arcslist) = + save pointsListB, pointsListE, pointsListO, pointsListD, labelPos, labelVec, labelAng, i, j, k, allArcsDefined, beginFree, endFree, pointsListCrv; + string pointsListB[], pointsListE[], pointsListO[]; + numeric i, j, k, pointsListD[], labelAng; + boolean allArcsDefined, beginFree, endFree; + color labelVec; + pair labelPos; + path pointsListCrv[]; + image( + if (textLabels): + i := -1; + allArcsDefined := true; + forsuffixes aN=arcslist: + i := i + 1; + pointsListB[i] := getAttribute("arc", "BName", str aN); + pointsListE[i] := getAttribute("arc", "EName", str aN); + pointsListO[i] := getAttribute("arc", "CenterName", str aN); + pointsListR[i] := getAttribute("arc", "Diameter", str aN); + begingroup + save thirdDimension; + pointsListCrv[i] := subpath + (getAttribute("arc", "Begin", str aN), getAttribute("arc", "End", str aN)) + of byCirclePathGenerate (getAttribute("arc", "SpaceRotation", str aN), getAttribute("arc", "PlaneVector", str aN)); + endgroup; + if (getAttribute("arc", "BName", str aN) = "") + or (getAttribute("arc", "EName", str aN) = ""): + allArcsDefined := false; + fi; + endfor; + if allArcsDefined: + for j := 0 step 1 until i: + beginFree := true; + endFree := true; + for k := 0 step 1 until i: + if (j <> k): + if (pointsListB[j] = pointsListE[k]): + beginFree := false; + fi; + if (pointsListE[j] = pointsListB[k]): + endFree := false; + fi; + fi; + endfor; + if beginFree: + labelAng := angle(direction 0 of pointsListCrv[j]) + 180; + labelPos := ((point 0 of pointsListCrv[j]) * pointsListR[j]) + scantokens(pointsListO[j]); + draw byTextLabel(pointLabel)( + pointsListB[j], + labelPos, + labelAng, + textLabelShift); + fi; + if endFree: + labelAng := angle(direction 0 of reverse(pointsListCrv[j])) + 180; + labelPos := ((point 0 of reverse(pointsListCrv[j])) * pointsListR[j]) + scantokens(pointsListO[j]); + draw byTextLabel(pointLabel)( + pointsListE[j], + labelPos, + labelAng, + textLabelShift); + fi; + endfor; + fi; + fi; + ) +enddef; + +vardef byCircleDefineFree@#(expr o, r, col, dp, th, s) = +if str @# = "": + byCircleDefineFree.aCircle(o, r, col, dp, th, s); +else: + setAttribute("circle", "Color", str @#, col); + setAttribute("circle", "Dashed", str @#, dp); + setAttribute("circle", "Thin", str @#, th); + setAttribute("circle", "Shift", str @#, s); + setAttribute("circle", "Diameter", str @#, 2r); + setAttribute("circle", "Center", str @#, o); + setAttribute("circle", "CenterName", str @#, ""); + setAttribute("circle", "AName", str @#, ""); + setAttribute("circle", "BName", str @#, ""); + setAttribute("circle", "CName", str @#, ""); + setAttribute("circle", "SpaceRotation", str @#, (0,0,0)); + setAttribute("circle", "PlaneVector", str @#, (0,0,1)); + setAttribute("circle", "Synonym", str @#, str @#); +fi; +enddef; + +vardef byCircleDefineR@#(suffix o)(expr r, col, dp, th, s) = +if str @# = "": + byCircleDefineR.scantokens(str o)(o)(r, col, dp, th, s) +else: + byCircleDefineFree.@#(o, r, col, dp, th, s); + setAttribute("circle", "CenterName", str @#, str o); +fi; +enddef; + +vardef byCircleR@#(suffix o)(expr r, col, dp, th, s) = +if str @# = "": + byCircleR.scantokens(str o)(o)(r, col, dp, th, s) +else: + byCircleDefineR.@#(o, r, col, dp, th, s); + byNamedCircle(@#) +fi +enddef; + +vardef byCircleDefine@#(suffix o, a)(expr col, dp, th, s) = +if str @# = "": + byCircleDefine.scantokens(str o & str a)(o, a)(col, dp, th, s) +else: + byCircleDefineR.@#(o, abs(a-o), col, dp, th, s); + setAttribute("circle", "AName", str @#, str a); +fi; +enddef; + +vardef byCircle@#(suffix o, a)(expr col, dp, th, s) = +if str @# = "": + byCircle.scantokens(str o & str a)(o, a)(col, dp, th, s) +else: + byCircleDefine.@#(o, a, col, dp, th, s); + byNamedCircle(@#) +fi +enddef; + +vardef byCircleABC@#(suffix a, b, c)(expr col, dp, th, s) = +if str @# = "": + byCircleABC.scantokens(str a & str b & str c)(a, b, c)(col, dp, th, s) +else: + save o, r; + pair o; + numeric r; + o := byFindCircleCenterABC(a, b, c); + if byIsPointInSpace(a, b, c): + r := absXYZ(pointXYZ.a - byFindCircleCenterABCinSpace(pointXYZ.a, pointXYZ.b, pointXYZ.c)); + else: + r := abs(o - a); + fi; + byCircleDefineFree.@#(o, r, col, dp, th, s); + setAttribute("circle", "AName", str @#, str a); + setAttribute("circle", "BName", str @#, str b); + setAttribute("circle", "CName", str @#, str c); + if byIsPointInSpace(a, b, c): + save pv; + color pv; + pv := unitvectorXYZ((pointXYZ.a - pointXYZ.b) crossproduct (pointXYZ.c - pointXYZ.b)); + setAttribute("circle", "PlaneVector", str @#, pv); + fi; + byNamedCircle(@#) +fi +enddef; + +vardef byFindCircleCenterABC (suffix a, b, c) = + save rv; + pair rv; + if byIsPointInSpace(a, b, c): + save rvInSpace; + color rvInSpace; + rvInSpace := byFindCircleCenterABCinSpace(pointXYZ.a, pointXYZ.b, pointXYZ.c) colorrotated projectionAngle; + rv := (redpart(rvInSpace), greenpart(rvInSpace)); + else: + rv = whatever[1/2[a, b], 1/2[a, b] + ((a - b) rotated 90)] + = whatever[1/2[b, c], 1/2[b, c] + ((b - c) rotated 90)]; + fi; + rv +enddef; + +vardef byFindCircleCenterABCinSpace (expr a, b, c) = + save vec; + color vec[]; + vec0 := ((a - b) crossproduct (c - b)); + vec1 := byRotateAroundAxis (a - b, (0, 0, 0), 90, vec0); + vec2 := byRotateAroundAxis (c - b, (0, 0, 0), 90, vec0); + vec3 = whatever[1/2[a, b], 1/2[a, b] + vec1] + = whatever[1/2[b, c], 1/2[b, c] + vec2]; % Fails from time to time because of some roundoff errors + vec3 +enddef; + +vardef byReturnCircleCenter (expr circleName) = + if (getAttribute("circle", "CenterName", circleName) <> ""): + scantokens(getAttribute("circle", "CenterName", circleName)) + elseif (getAttribute("circle", "BName", circleName) <> ""): + byFindCircleCenterABC( + scantokens(getAttribute("circle", "AName", circleName)), + scantokens(getAttribute("circle", "BName", circleName)), + scantokens(getAttribute("circle", "CName", circleName)) + ) + else: + getAttribute("circle", "Center", circleName) + fi +enddef; + +vardef byNamedCircle (text circlesList) = + save circlecenter; + pair circlecenter; + image( + forsuffixes cN=circlesList: + circlecenter := byReturnCircleCenter(str cN); + draw byArcRender ( + circlecenter, 0, 8, + getAttribute("circle", "Diameter", str cN), + getAttribute("circle", "SpaceRotation", str cN), + getAttribute("circle", "PlaneVector", str cN), + getAttribute("circle", "Color", str cN), + getAttribute("circle", "Dashed", str cN), + getAttribute("circle", "Thin", str cN), + getAttribute("circle", "Shift", str cN), 0); + if (textLabels and autoLabelingMode): + if (getAttribute("circle", "CenterName", str cN) <> ""): + draw byTextLabel(pointLabel)( + getAttribute("circle", "CenterName", str cN), + scantokens(getAttribute("circle", "CenterName", str cN)), + 0, 0); + fi; + if (getAttribute("circle", "AName", str cN) <> ""): + draw byLabelCircle(getAttribute("circle", "AName", str cN), str cN); + fi; + if (getAttribute("circle", "BName", str cN) <> ""): + draw byLabelCircle(getAttribute("circle", "BName", str cN), str cN); + fi; + if (getAttribute("circle", "CName", str cN) <> ""): + draw byLabelCircle(getAttribute("circle", "CName", str cN), str cN); + fi; + fi; + endfor; + ) +enddef; + +% +% Arbitraty figures +% + +vardef byArbitraryFigureDefine@#(expr p, col, dp, th) = +if str @# = "": + byArbitraryFigureDefine.anArbitraryFigure(p, col, dp, th); +else: + setAttribute("arbitraryFigure", "Path", str @#, p); + setAttribute("arbitraryFigure", "Color", str @#, col); + setAttribute("arbitraryFigure", "Dashed", str @#, dp); + setAttribute("arbitraryFigure", "Thin", str @#, th); + setAttribute("arbitraryFigure", "Synonym", str @#, str @#); +fi; +enddef; + +vardef byArbitraryFigure@#(expr p, col, dp, th) = +if str @# = "": + byArbitraryFigure.anArbitraryFigure(p, col, dp, th) +else: + byArbitraryFigureDefine@#(p, col, dp, th); + byNamedArbitraryFigure(@#) +fi +enddef; + +vardef byNamedArbitraryFigure (text arbitraryFiguresList) = + save p; + path p; + image( + forsuffixes pN=arbitraryFiguresList: + p := getAttribute("arbitraryFigure", "Path", str pN); + draw byArbitraryFigureRender + (p, getAttribute("arbitraryFigure", "Color", str pN), + getAttribute("arbitraryFigure", "Dashed", str pN), + getAttribute("arbitraryFigure", "Thin", str pN)); + endfor; + ) rotated globalRotation +enddef; + +vardef byArbitraryFigureRender (expr p, col, dp, th) = + image( + draw (p scaled scaleFactor) + withpen pencircle scaled lineThickness(th) + withcolor col + if (dp > 0): + dashed byDashPattern(arclength(p), dp) + fi; + ) +enddef; + +vardef byArbitraryCurveDefine@#(text pointsList)(expr col, dp, th) = +if str @# = "": + byArbitraryCurveDefine.anArbitraryCurve(pointsList)(col, dp, th); +else: + save pL, p; + string pL; + path p; + pL := ""; + p := byListToPath(pointsList); + forsuffixes pN=pointsList: + appendList(pL, str pN, 1, false); + endfor; + setAttribute("arbitraryCurve", "PointsList", str @#, pL); + setAttribute("arbitraryCurve", "Path", str @#, p); + setAttribute("arbitraryCurve", "Color", str @#, col); + setAttribute("arbitraryCurve", "Dashed", str @#, dp); + setAttribute("arbitraryCurve", "Thin", str @#, th); + setAttribute("arbitraryCurve", "Synonym", str @#, str @#); +fi; +enddef; + +vardef byNamedArbitraryCurve (text curvesList) = + save p; + path p; + image( + forsuffixes cN=curvesList: + p := byListToPath(scantokens(getAttribute("arbitraryCurve", "PointsList", str cN))); + draw byArbitraryFigureRender + (p, getAttribute("arbitraryCurve", "Color", str cN), + getAttribute("arbitraryCurve", "Dashed", str cN), + getAttribute("arbitraryCurve", "Thin", str cN)) rotated globalRotation; + if (autoLabelingMode): + if (textLabels): + draw byLabelsOnPolygon(scantokens(getAttribute("arbitraryCurve", "PointsList", str cN)), noPoint)(1, 0); + fi; + fi; + endfor; + ) +enddef; + +vardef byListToPath(text pointsList) = + save pathString, rp; + string pathString; + path rp; + pathString := ""; + forsuffixes pN=pointsList: + pathString := pathString & str pN & ".."; + endfor; + rp := scantokens(substring (0, length(pathString) - 2) of pathString); + rp +enddef; + +vardef byArbitraryCurve@#(text pointsList)(expr col, dp, th) = +if str @# = "": + byArbitraryCurve.anArbitraryCurve(pointsList)(col, dp, th) +else: + byArbitraryCurveDefine@#(pointsList)(col, dp, th); + byNamedArbitraryCurve(@#) +fi +enddef; + +% +% Filled figures +% + +vardef byFilledCircleSegment@#(suffix o)(expr r, b, e, col) = +if str @# = "": + byFilledCircleSegment.o(o, r, b, e, col) +else: + setAttribute("filledCircleSegment", "Color", str @#, col); + setAttribute("filledCircleSegment", "Diameter", str @#, 2r); + setAttribute("filledCircleSegment", "Begin", str @#, b); + setAttribute("filledCircleSegment", "End", str @#, e); + setAttribute("filledCircleSegment", "CenterName", str @#, str o); + setAttribute("filledCircleSegment", "Synonym", str @#, str @#); + byNamedFilledCircleSegment(@#) +fi +enddef; + +vardef byNamedFilledCircleSegment (text filledCircleSegmentList) = + save p; + path p; + image( + forsuffixes csN=filledCircleSegmentList: + fill ( + (subpath + (getAttribute("filledCircleSegment", "Begin", str csN), + getAttribute("filledCircleSegment", "End", str csN)) + of fullcircle) + --cycle) + scaled (getAttribute("filledCircleSegment", "Diameter", str csN)*scaleFactor) + shifted (scantokens(getAttribute("filledCircleSegment", "CenterName", str csN))*scaleFactor) + withcolor getAttribute("filledCircleSegment", "Color", str csN); + endfor; + ) rotated globalRotation +enddef; + +vardef byFilledCircleSector@#(suffix o)(expr r, b, e, col) = +if str @# = "": + byFilledCircleSector.o(o, r, b, e, col) +else: + setAttribute("filledCircleSector", "Color", str @#, col); + setAttribute("filledCircleSector", "Diameter", str @#, 2r); + setAttribute("filledCircleSector", "Begin", str @#, b); + setAttribute("filledCircleSector", "End", str @#, e); + setAttribute("filledCircleSector", "CenterName", str @#, str o); + byNamedFilledCircleSector(@#) +fi +enddef; + +vardef byNamedFilledCircleSector (text filledCircleSectorList) = + image( + forsuffixes csN=filledCircleSectorList: + fill ((subpath + (getAttribute("filledCircleSector", "Begin", str csN), + getAttribute("filledCircleSector", "End", str csN)) + of fullcircle) -- (0, 0) -- cycle) + scaled (getAttribute("filledCircleSector", "Diameter", str csN)*scaleFactor) + shifted (scantokens(getAttribute("filledCircleSector", "CenterName", str csN))*scaleFactor) + withcolor getAttribute("filledCircleSector", "Color", str csN); + endfor; + ) rotated globalRotation +enddef; + +vardef byPolygonDefine@#(text pointsList)(suffix col) = +if str @# = "": + byPolygonDefine.scantokens(byPolygonName(pointsList))(pointsList)(col); +else: + save pL, pLraw, p, tn; + path p; + string pL, pLraw; + numeric tn; + pL := ""; + pLraw := ""; + p := forsuffixes pN=pointsList: pN -- endfor cycle; + tn := turningnumber(p); + forsuffixes pN=pointsList: + appendList(pL, str pN, -tn, false); + appendList(pLraw, str pN, 1, false); + endfor; + setAttribute("polygon", "Color", str @#, col); + setAttribute("polygon", "ColorName", str @#, str col); + setAttribute("polygon", "Transparency", str @#, 1); + setAttribute("polygon", "PointsList", str @#, pL); + setAttribute("polygon", "PointsListRaw", str @#, pLraw); + setAttribute("polygon", "Synonym", str @#, str @#); +fi; +enddef; + +vardef byPolygonPathFromList (expr pointsList) = + forsuffixes pN=scantokens(pointsList): pN -- endfor cycle +enddef; + +vardef byPolygon@#(text pointsList)(suffix col) = +if str @# = "": + byPolygon.scantokens(byPolygonName(pointsList))(pointsList)(col) +else: + byPolygonDefine.@#(pointsList)(col); + byNamedPolygon(@#) +fi +enddef; + +vardef byPolygonName(text pointsList) = + save polygonName; + string polygonName; + polygonName := ""; + forsuffixes pN=pointsList: + polygonName := polygonName & str pN; + endfor; + polygonName +enddef; + +vardef byNamedPolygon (text polygonsList) = + save polyPath, polyCol, polyTransp, polyGhost, shadingFunctionExists; + path polyPath; + numeric polyTransp; + picture polyGhost; + boolean shadingFunctionExists; + image( + draw image( + forsuffixes pN=polygonsList: + polyPath := byPolygonPathFromList(getAttribute("polygon", "PointsList", str pN)) scaled scaleFactor; + defineColor.polyCol(getAttribute("polygon", "Color", str pN)); + polyTransp := getAttribute("polygon", "Transparency", str pN); + if known shadingFor.scantokens(getAttribute("polygon", "ColorName", str pN)): + shadingFunctionExists := true; + else: + shadingFunctionExists := false; + fi; + if (not isWhite(polyCol)): + if not shadingFunctionExists: + fill polyPath + withcolor + if polyTransp = 1: + polyCol + else: + transparent(1, polyTransp, polyCol) + fi; + else: + shadingFunction.scantokens(getAttribute("polygon", "ColorName", str pN)) + (polyPath, polyCol)(scantokens(getAttribute("polygon", "PointsListRaw", str pN))); + fi; + fi; + polyGhost := image( + if not shadingFunctionExists: + if (not isLight(polyCol)): + draw ghostPicture; + fi; + draw polyPath + withcolor polyCol + withpen pencircle scaled 2lineWidth; + fi; + ); + clip polyGhost to polyPath; + draw polyGhost; + if (isLight(polyCol)) and not shadingFunctionExists: + draw (byPolygonPathFromList(getAttribute("polygon", "PointsList", str pN)) scaled scaleFactor) + withcolor selectOutlineColor.scantokens(getAttribute("polygon", "ColorName", str pN)) + withpen pencircle scaled lineWidthThin; + fi; + if mainPictureMode and ghostLines: + save gP; + picture gP; + gP := ghostPicture; + ghostPicture := image( + draw gP; + draw polyPath withcolor white withpen pencircle scaled lineWidthHair; + ); + fi; + endfor; + ) rotated globalRotation; + if (autoLabelingMode): + draw byLabelPolygon(-1)(polygonsList); + fi; + ) +enddef; + +vardef byMergePolygons (text polygonsList) = + save pointsList, preList, postList, mergedList, points, postPoints, i, j, k, l, m, n, o, iteration, beforeJoin, polygonChecked, pointChecked; + string pointsList[], preList[], postList[], mergedList, points[], postPoints[]; + numeric l, m, n, o, iteration; + boolean beforeJoin, polygonChecked[], pointChecked[]; + n := -1; + forsuffixes pN=polygonsList: + n := n + 1; + pointsList[n] := getAttribute("polygon", "PointsList", str pN); + polygonChecked[n] := false; + endfor; + if (n > 0): + iteration := -1; + m := -1; + forever: + preList0 := ""; + postList0 := ""; + beforeJoin := true; + iteration := iteration + 1; + l := -1; + forsuffixes i = scantokens(pointsList0): + l := l + 1; + if (l > m): + pointChecked[l] := false; + fi; + if (beforeJoin): + preList0 := preList0 & ", " & str i; + if (not pointChecked[l]): + for j := 1 step 1 until n: + if (beforeJoin and not polygonChecked[j]): + preList1 := ""; + postList1 := ""; + forsuffixes k=scantokens(pointsList[j]): + if (beforeJoin): + preList1 := preList1 & ", " & str k; + if (k = i): + beforeJoin := false; + polygonChecked[j] := true; + pointChecked[l] := true; + o := l; + fi; + else: + postList1 := postList1 & ", " & str k; + fi; + endfor; + fi; + endfor; + fi; + else: + postList0 := postList0 & ", " & str i; + fi; + endfor; + if (not beforeJoin): + mergedList := preList0 & postList1 & preList1 & postList0; + mergedList := substring (2, length(mergedList)) of mergedList; + pointsList0 := polygonCleanup(scantokens(mergedList)); + if (length(pointsList0) < length(mergedList)): + pointChecked[o] := false; + mergedList := pointsList0; + fi; + m := l; + fi; + exitif(beforeJoin or (iteration > 100)); + endfor; + else: + mergedList := pointsList0; + fi; + mergedList +enddef; + +vardef polygonCleanup (text pointsList) = + save cleanedList, points, m, n, iteration; + string cleanedList, points[]; + numeric m, n, iteration; + n := -1; + forsuffixes pL = pointsList: + n := n + 1; + points[n] := str pL; + endfor; + iteration := -1; + forever: + iteration := iteration + 1; + m := 0; + for i := 0 step 1 until n: + if (points[cycleval(i-2, n + 1)] = points[i]): + m := m + 2; + else: + points[cycleval(i-m, n + 1)] := points[i]; + fi; + endfor; + n := n - m; + exitif((m = 0) or (iteration > 100)); + endfor; + for i := 0 step 1 until n: + appendList(cleanedList, points[i], 1, false); + endfor; + cleanedList +enddef; + +% +% Angles +% + +vardef byAngleDefine@#(suffix a, b, c)(expr col, sty) = +if str @# = "": + byAngleDefine.scantokens(str a & str b & str c)(a, b, c)(col, sty); +else: + if mainPictureMode: + appendList(pointAnglesList.b, str @#, 1, true); + appendList(anglePointsList, str b, 1, true); + fi; + save p, q, d, ad, ac, at; + path p; + picture q; + numeric ad, at; + boolean ac; + if autoRightAngles: + if isRightAngle(a, b, c): + at := 1; + else: + at := 0; + fi; + else: + at := 0; + fi; + p := byConstructAngleArc(a, b, c, at); + ad := angle(point (arctime arclength(p)/2 of p) of p); + if (ypart(unitvector(a-b) rotated -ad)) > 0: + ac := true; + else: + ac := false; + fi; + setAttribute("angle", "Arc", str @#, p); + setAttribute("angle", "ArcClockwise", str @#, ac); + setAttribute("angle", "ArcType", str @#, at); + setAttribute("angle", "Val", str @#, 180*arclength(p scaled 2)/pi); + setAttribute("angle", "AVal", str @#, angle(a-b)); + setAttribute("angle", "CVal", str @#, angle(c-b)); + setAttribute("angle", "Direction", str @#, ad); + setAttribute("angle", "Color", str @#, col); + setAttribute0("angle", "OptionalColor", str @#, byDefaultAngleOptionalColor); + setAttribute("angle", "Style", str @#, sty); + setAttribute("angle", "AName", str @#, str a); + setAttribute("angle", "BName", str @#, str b); + setAttribute("angle", "CName", str @#, str c); + setAttribute("angle", "ScaleCorrection", str @#, 1); + setAttribute("angle", "Standalone", str @#, 0); + setAttribute("angle", "Synonym", str @#, str @#); +fi; +enddef; + +vardef byConsiderAngleRight(suffix angleName) = + setAttribute("angle", "ArcType", str angleName, 1); +enddef; + +vardef byConstructAngleArc (suffix a, b, c)(expr s) = + save p, d, cutTime, q; + pair d; + path p[]; + color q[]; + numeric cutTime[]; + d := 1/2[unitvector(a-b),unitvector(c-b)]; + if (abs(d) = 0): + d := (unitvector(a-c) rotated 90); + fi; + if (s = 0): + p0 := fullcircle; + if (attributeExists("point", "XYZ", str a) + and attributeExists("point", "XYZ", str b) + and attributeExists("point", "XYZ", str c)): + p0 := byFullCircleToPlane( + getAttribute("point", "XYZ", str a), + getAttribute("point", "XYZ", str b), + getAttribute("point", "XYZ", str c) + ); + fi; + cutTime1 := xpart((p0 scaled angleSize) intersectiontimes ((0, 0)--(unitvector(a - b) scaled 2angleSize))); + cutTime2 := xpart((p0 scaled angleSize) intersectiontimes ((0, 0)--(unitvector(c - b) scaled 2angleSize))); + p1 := subpath (cutTime1, cutTime2) of p0; + p2 := subpath (cutTime1 - length(p0), cutTime2) of p0; + p3 := subpath (cutTime1 + length(p0), cutTime2) of p0; + if (arclength(p2) > arclength(p3)): + p4 := p3; + else: + p4 := p2; + fi; + if (arclength(p1) < arclength(p4)): + p4 := p1; + fi; + elseif (s = 1): + if (attributeExists("point", "XYZ", str a) + and attributeExists("point", "XYZ", str b) + and attributeExists("point", "XYZ", str c)): + q1 := unitvectorXYZ(pointXYZ.a - pointXYZ.b) colorrotated projectionAngle; + q3 := unitvectorXYZ(pointXYZ.c - pointXYZ.b) colorrotated projectionAngle; + q2 := q1 + q3; + p4 := 1/2(redpart(q1), greenpart(q1)) + -- 1/2(redpart(q2), greenpart(q2)) + -- 1/2(redpart(q3), greenpart(q3)); + else: + p4 := 1/2unitvector(a - b) + -- 1/2(unitvector(a - b) + unitvector(c - b)) + -- 1/2unitvector(c - b); + fi; + fi; + p4 +enddef; + +vardef byIsArcClockwise (expr p) = + save rv; + boolean rv; + if ypart(direction (1/2length(p)) of (p rotated -angle(point 1/2length(p) of p))) < 0: + rv := true; + else: + rv := false; + fi; + rv +enddef; + +vardef byFullCircleToPlane (expr colPlaneA, colPlaneB, colPlaneC) = + save dvec, fvec, p; + color dvec; + pair fvec; + path p; + dvec := unitvectorXYZ((colPlaneA-colPlaneB) crossproduct (colPlaneC-colPlaneB)); + dvec := dvec colorrotated projectionAngle; + fvec := (redpart dvec, greenpart dvec); + if (abs(fvec) > 0): + p := ((fullcircle rotated -angle(fvec)) xscaled sqrt(1-abs(fvec)**2)) rotated angle(fvec); + else: + p := fullcircle; + fi; + p +enddef; + +vardef byAngle@#(suffix a, b, c)(expr col, sty) = +if str @# = "": + byAngle.scantokens(str a & str b & str c)(a, b, c, col, sty) +else: + byAngleDefine@#(a, b, c, col, sty); + byNamedAngle(@#) +fi +enddef; + +vardef byAngleDefineExtended@#(suffix a, b, c)(expr col, sty)(text optionalColors) = +if str @# = "": + byAngleDefineExtended.scantokens(str a & str b & str c)(a, b, c)(col, sty)(optionalColors); +else: + save n; + numeric n; + n := 0; + byAngleDefine.@#(a, b, c, col, sty); + forsuffixes oC=optionalColors: + setAttribute[n]("angle", "OptionalColor", str @#, oC); + n := n + 1; + endfor; +fi; +enddef; + +vardef byAngleExtended@#(suffix a, b, c)(expr col, sty)(text optionalColors) = +if str @# = "": + byAngleExtended.scantokens(str a & str b & str c)(a, b, c)(col, sty)(optionalColors) +else: + byAngleDefineExtended@#(a, b, c)(col, sty)(optionalColors); + byNamedAngle(@#) +fi +enddef; + +vardef byNamedAngle (text anglesList) = + save p, q, processedAL; + picture q; + path p; + string processedAL; + image( + processedAL := byProcessAnglesList(anglesList); + forsuffixes aN=scantokens(processedAL): + setAttribute("angle", "ScaleCorrection", str aN, angleScale); + p := byConstructAngleArc( + scantokens(getAttribute("angle", "AName", str aN)), + scantokens(getAttribute("angle", "BName", str aN)), + scantokens(getAttribute("angle", "CName", str aN)), + getAttribute("angle", "ArcType", str aN)); + q := image( + if (getAttribute("angle", "Color", str aN) <> white): + if (typeOf(getAttribute("angle", "Style", str aN)) = "numeric"): + draw scantokens(byAngleMacroName[getAttribute("angle", "Style", str aN)]) (p, getAttribute("angle", "Color", str aN), angleOptionalColor.scantokens(angleSynonym.aN)); + elseif (typeOf(getAttribute("angle", "Style", str aN)) = "string"): + draw scantokens("byAngleM" & getAttribute("angle", "Style", str aN)) (p, getAttribute("angle", "Color", str aN), angleOptionalColor.scantokens(angleSynonym.aN)); + fi; + else: + draw byAngleMWhite(p, 0, 0); + fi; + if solidAngleMode and (textLabels and autoLabelingMode): + draw (p scaled (1/3angleScale*angleSize))--(0,0)--cycle withcolor white; + fill (p scaled (1/3angleScale*angleSize))--(0,0)--cycle withcolor white; + fi; + ); + draw (q shifted (scantokens(getAttribute("angle", "BName", str aN)) scaled scaleFactor)) rotated globalRotation; + endfor; + ) +enddef; + +vardef byNamedSolidAngle (text anglesList) = + save solidAngleMode, processedAL, centerName, rv; + boolean solidAngleMode; + string processedAL, centerName; + processedAL := byProcessAnglesList(anglesList); + solidAngleMode := true; + image( + forsuffixes aN := scantokens(processedAL): + centerName := getAttribute("angle", "BName", str aN); + endfor; + draw byNamedAngle(anglesList); + if (textLabels and autoLabelingMode): + draw byTextLabel(pointLabel)(centerName, scantokens(centerName), 0, 0); + fi; + ) +enddef; + +vardef byProcessAnglesList@#(text anglesListToProcess)= + save processedAnglesList, compoundAngleString, splitAnglePoints; + string processedAnglesList, compoundAngleString, splitAnglePoints; + processedAnglesList := ""; + splitAnglePoints := ""; + forsuffixes aN=anglesListToProcess: + if attributeExists("angle", "Synonym", str aN): + appendList(processedAnglesList, str aN, 1, false); + else: + splitAnglePoints := bySplitStringIntoAngles(str aN); + if (known aN) and (length(splitAnglePoints) = 0): + compoundAngleString := pointAnglesList.aN; + else: + compoundAngleString := byConstructCompoundAngle(scantokens(splitAnglePoints)); + fi; + if compoundAngleString <> "": + appendList(processedAnglesList, compoundAngleString, 1, false); + if str @# <> "": + appendList(@#, splitAnglePoints, 1, false); + fi; + else: + errmessage("Unknown angle: " & str aN); + fi; + fi; + endfor; + processedAnglesList +enddef; + +string byAngleMacroName[]; +byAngleMacroName[-2] := "byAngleMWhite"; +byAngleMacroName[-1] := "byAngleMThinLine"; +byAngleMacroName[0] := "byAngleMSolid"; +byAngleMacroName[1] := "byAngleMLine"; +byAngleMacroName[2] := "byAngleMDashedLine"; + +vardef byAngleMWhite (expr angleArc, angleColor)(suffix angleOptionalColors) = + save p; + path p; + p := angleArc scaled ((angleScale*angleSize) - lineWidthThin); + image( + fill ((0, 0)--p--cycle) withcolor white; + draw p withpen pencircle scaled lineWidthThin withcolor black; + ) +enddef; + +vardef byAngleMSolid (expr angleArc, angleColor)(suffix angleOptionalColors) = + save p; + path p; + p := angleArc scaled (angleScale*angleSize); + image( + fill ((0, 0)--p--cycle) withcolor angleColor; + ) +enddef; + +vardef byAngleMLine (expr angleArc, angleColor)(suffix angleOptionalColors) = + save p, rv; + path p[]; + picture rv; + p0 := angleArc scaled ((angleScale*angleSize) - lineWidth); + p1 := (0, 0)--p0--cycle; + p2 := (0, 0)--(angleArc scaled (angleScale*angleSize))--cycle; + rv := image( + if (angleOptionalColors[0] <> bytransparent): + fill p2 withcolor angleOptionalColors[0]; + fi; + draw p0 withpen pencircle scaled lineWidth withcolor angleColor; + ); + clip rv to p2; + rv +enddef; + +vardef byAngleMThinLine (expr angleArc, angleColor)(suffix angleOptionalColors) = + save p; + path p; + p := angleArc scaled ((angleScale*angleSize) - lineWidthThin); + image( + if (angleOptionalColors[0] <> bytransparent): + fill ((0, 0)--p--cycle) withcolor angleOptionalColors[0]; + fi; + draw p withpen pencircle scaled lineWidthThin withcolor angleColor; + ) +enddef; + +vardef byAngleMDashedLine (expr angleArc, angleColor)(suffix angleOptionalColors) = + save p, q; + path p; + p := angleArc scaled ((angleScale*angleSize) - lineWidth); + image( + if (angleOptionalColors[0] <> bytransparent): + fill (p--(0,0)--cycle) withcolor angleOptionalColors[0]; + fi; + draw p withpen pencircle scaled lineWidth withcolor angleColor dashed byDashPattern(arclength(p), 1); + draw (0,0); % draws nothing. for some reason dashing seems to "leak" without this. + ) +enddef; + +% Following monstrosity draws dummy sides for angles which require them, +% essentially, it takes a list of angles, checks if they all are constructed +% on one point; then it browses through all angles, finds their sides +% and counts them; ones that occur twice it draws as is; ones that occur +% once it shifts inside an angle; and after that it clips everything +% to angle outline. +vardef byNamedAngleSidesFull (text anglesList)(text linesList)= + save centerName, uniqueCenter, angleGenArc, angleDir, angleCount, angleType, angleSide, anglePart, angleNum, angleCheck, + ac, as, p, n, compoundAnglePath, compoundAngleArc, b, e, outputImage, lineCol, lineTh, lineDash, pointName, scaleToUse, + processedAnglesList, processedLinesList, + planePoints, threeDMode, projectedCircle, labelVector, + enteredAnglePointsList, pointNameToUse; + string centerName, pointName[], processedAnglesList, processedLinesList,enteredAnglePointsList, pointNameToUse; + pair ac, p[], labelVector; + boolean uniqueCenter, angleCheck, doNotLabelCenter, threeDMode; + numeric angleDir[], angleCount[], angleType[], angleSide[], angleNum, anglePart[], as[], n[], b, e, lineTh, lineDash, scaleToUse; + path angleGenArc, compoundAnglePath, compoundAngleArc, projectedCircle; + color planePoints[]; + picture outputImage; + centerName := ""; + uniqueCenter := true; + doNotLabelCenter := false; + scaleToUse := -1; + enteredAnglePointsList := ""; + processedAnglesList := byProcessAnglesList.enteredAnglePointsList(anglesList); + forsuffixes lN=linesList: + appendList(processedLinesList, str lN, 1, false); + endfor; + threeDMode := false; + forsuffixes aN=scantokens(processedAnglesList): + ac := scantokens(getAttribute("angle", "BName", str aN)); + if (centerName = ""): + centerName := getAttribute("angle", "BName", str aN); + if (attributeExists("point", "XYZ", getAttribute("angle", "AName", str aN)) + and attributeExists("point", "XYZ", getAttribute("angle", "BName", str aN)) + and attributeExists("point", "XYZ", getAttribute("angle", "CName", str aN))): + threeDMode := true; + planePoints1 := getAttribute("point", "XYZ", getAttribute("angle", "AName", str aN)); + planePoints2 := getAttribute("point", "XYZ", getAttribute("angle", "BName", str aN)); + planePoints3 := getAttribute("point", "XYZ", getAttribute("angle", "CName", str aN)); + projectedCircle := byFullCircleToPlane(planePoints1, planePoints2, planePoints3); + else: + projectedCircle := fullcircle; + fi; + elseif (centerName <> getAttribute("angle", "BName", str aN)): + uniqueCenter := false; + elseif threeDMode: + if (not isInPlane(getAttribute("point", "XYZ", getAttribute("angle", "AName", str aN)), + planePoints1, planePoints2, planePoints3)) + or (not isInPlane(getAttribute("point", "XYZ", getAttribute("angle", "CName", str aN)), + planePoints1, planePoints2, planePoints3)): + errmessage("The angles " & processedAnglesList & " are not coplanar"); + fi; + fi; + if (getAttribute("angle", "Standalone", str aN) > 1): + doNotLabelCenter := true; + fi; + if (getAttribute("angle", "ScaleCorrection", str aN) <> angleScale) + and (getAttribute("angle", "ScaleCorrection", str aN) > scaleToUse): + scaleToUse := getAttribute("angle", "ScaleCorrection", str aN); + fi; + endfor; + if (length(processedLinesList) = 0): + if (known pointLinesList.scantokens(centerName)): + processedLinesList := pointLinesList.scantokens(centerName); + fi; + fi; + if (scaleToUse < 0): + scaleToUse := angleScale; + fi; + outputImage := image( + startTempAngleScale(scaleToUse); + if (uniqueCenter): + angleNum := 0; + forsuffixes aN=scantokens(processedAnglesList): + angleGenArc := byConstructAngleArc( + scantokens(getAttribute("angle", "AName", str aN)), + scantokens(getAttribute("angle", "BName", str aN)), + scantokens(getAttribute("angle", "CName", str aN)), + getAttribute("angle", "ArcType", str aN)); + anglePart1 := angle(point 0 of angleGenArc); + as1 := sign(ypart((direction 0 of angleGenArc) rotated -anglePart1)); + anglePart2 := angle(point 0 of reverse(angleGenArc)); + as2 := sign(ypart((direction 0 of reverse(angleGenArc)) rotated -anglePart2)); + for j=1,2: + angleCheck := true; + for i := 1 step 1 until angleNum: + if ((abs(angleDir[i] - anglePart[j]) mod 360) < 1): + angleCount[i] := angleCount[i] + 1; + angleCheck := false; + if ((getAttribute("angle", "Style", str aN) = 0) + or ((getAttribute0("angle", "OptionalColor", str aN) <> white) + and (getAttribute0("angle", "OptionalColor", str aN) <> bytransparent))) + and (getAttribute("angle", "ArcType", str aN) <> 1): + angleType[i] := 0; + fi; + fi; + endfor; + if angleCheck: + angleNum := angleNum + 1; + angleDir[angleNum] := anglePart[j]; + angleSide[angleNum] := as[j]; + if ((getAttribute("angle", "Style", str aN) <> 0) + and (isWhite(getAttribute0("angle", "OptionalColor", str aN)) + or (getAttribute0("angle", "OptionalColor", str aN) = bytransparent))) + or ((getAttribute("angle", "Style", str aN) = 0) + and isWhite(getAttribute("angle", "Color", str aN))) + or (getAttribute("angle", "ArcType", str aN) = 1): + angleType[angleNum] := 1; + else: + angleType[angleNum] := 0; + fi; + if (j = 1): + if (getAttribute("angle", "Standalone", str aN) > 0): + pointName[angleNum] := ""; + else: + pointName[angleNum] := getAttribute("angle", "AName", str aN); + fi; + elseif (j = 2): + if (getAttribute("angle", "Standalone", str aN) > 0): + pointName[angleNum] := ""; + else: + pointName[angleNum] := getAttribute("angle", "CName", str aN); + fi; + fi; + angleCount[angleNum] := 1; + fi; + endfor; + endfor; + j := 0; + for i := 1 step 1 until angleNum: + if (angleCount[i] = 1): + j := j + 1; + n[j] := i; + fi; + if (angleType[i] <> 0): + p1 := (ac*scaleFactor); + p2 := ((dir(angleDir[i]) scaled (1/2angleSize * angleScale))) shifted p1; + defineColor.lineCol(black); + lineDash := 0; + lineTh := 1; + forsuffixes lN=scantokens(processedLinesList): + if (getAttribute("line", "EndAName", str lN) = centerName): + if (abs(angle( + scantokens(getAttribute("line", "EndBName", str lN)) + - scantokens(getAttribute("line", "EndAName", str lN))) + - angleDir[i]) < 1): + defineColor.lineCol(getAttribute("line", "Color", str lN)); + lineDash := getAttribute("line", "Dashed", str lN); + lineTh := getAttribute("line", "Thin", str lN); + fi; + elseif (getAttribute("line", "EndBName", str lN) = centerName): + if (abs(angle( + scantokens(getAttribute("line", "EndAName", str lN)) + - scantokens(getAttribute("line", "EndBName", str lN))) + - angleDir[i]) < 1): + defineColor.lineCol(getAttribute("line", "Color", str lN)); + lineDash := getAttribute("line", "Dashed", str lN); + lineTh := getAttribute("line", "Thin", str lN); + fi; + fi; + endfor; + if (angleCount[i] > 1): + draw byLineRender (p1, p2, lineCol, lineDash, lineTh, p1, p2, t, 0, 0, 1); + else: + draw byLineRender (p1, p2, lineCol, lineDash, lineTh, p1, p2, 0, 0, -angleSide[i], 1); + fi; + fi; + endfor; + if (j = 2): + if not threeDMode: + b := (angleDir[n[1]]/360)*8; + e := (angleDir[n[2]]/360)*8; + else: + b := xpart(projectedCircle intersectiontimes ((0, 0) -- dir(angleDir[n[1]])*10)); + e := xpart(projectedCircle intersectiontimes ((0, 0) -- dir(angleDir[n[2]])*10)); + fi; + if (b > e): b := b - length(projectedCircle); fi; + if (angleSide[n[1]] > angleSide[n[2]]): + compoundAngleArc := ((subpath (b, e) of projectedCircle) scaled (angleScale*angleSize)) shifted (ac*scaleFactor); + else: + compoundAngleArc := ((subpath (b + 8, e) of projectedCircle) scaled (angleScale*angleSize)) shifted (ac*scaleFactor); + fi; + compoundAnglePath := compoundAngleArc -- (ac*scaleFactor) -- cycle; + elseif (j <> 0): + errmessage("There are gaps in a compound angle"); + fi; + else: + errmessage("The angles are not concentric"); + fi; + stopTempAngleScale; + ); + if (known compoundAnglePath): + clip outputImage to compoundAnglePath; + fi; + image( + startTempAngleScale(scaleToUse); + draw outputImage rotated globalRotation; + if (textLabels and autoLabelingMode) and (j=2) and (known compoundAnglePath): + for i=1,2: + if not threeDMode: + labelVector := dir(angleDir[n[i]]); + else: + labelVector := (projectedCircle intersectionpoint ((0, 0) -- dir(angleDir[n[i]])*10)) scaled 2; + fi; + pointNameToUse := pointName[n[i]]; + if length(enteredAnglePointsList) > 0: + show(enteredAnglePointsList); + forsuffixes j = scantokens(enteredAnglePointsList): + if (str j <> centerName): + if ((abs( + angle(scantokens(pointNameToUse) - scantokens(centerName)) + - angle(j - scantokens(centerName)) + ) mod 360) < 1): + pointNameToUse := str j; + fi; + fi; + endfor; + fi; + draw byTextLabel(pointLabel)(pointNameToUse, + ac + (labelVector*1/2angleSize*angleScale)/scaleFactor, angleDir[n[i]], textLabelShift); + endfor; + if not doNotLabelCenter: + draw byTextLabel(pointLabel)(centerName, ac, angle((point (arctime(1/2arclength(compoundAngleArc)) of compoundAngleArc) of compoundAngleArc) shifted (-ac*scaleFactor)) + 180, textLabelShift); + fi; + fi; + stopTempAngleScale; + ) +enddef; + +vardef byNamedAngleDummySides(text anglesList)= + byNamedAngleSidesFull(anglesList)(noLine) +enddef; + +vardef byNamedAngleSides(text anglesList)(text linesList) = + image( + draw byNamedAngle(anglesList); + draw byNamedAngleSidesFull(anglesList)(linesList); + ) +enddef; + +vardef byNamedAngleWithDummySides(text anglesList)= + image( + draw byNamedAngle(anglesList); + draw byNamedAngleDummySides(anglesList); + ) +enddef; + +vardef byNamedAngleResized (text anglesToUse) = + save modAnglesList, modAnglesListNew, anglesToDraw, anglesAdded, angleIsAdded, useThisAngle, sc, v, numOfAngles, i, j, k; + string modAnglesList, modAnglesListNew, anglesToDraw; + numeric anglesAdded, sc, v, numOfAngles; + boolean angleIsAdded, useThisAngle; + numOfAngles := 0; + forsuffixes k=anglesToUse: + if (length(str k) > 0): + if typeOf(getAttribute("angle", "Synonym", str k)) <> "string": + errmessage("Angle " & str k & " is missing"); + fi; + numOfAngles := numOfAngles + 1; + fi; + endfor; + image( + forsuffixes i=scantokens(anglePointsList): + modAnglesList := pointAnglesList.i; + modAnglesListNew := ""; + forsuffixes j=scantokens(modAnglesList): + if (numOfAngles > 0): + useThisAngle := false; + forsuffixes k=anglesToUse: + if (getAttribute("angle", "Synonym", str j) = getAttribute("angle", "Synonym", str k)): + useThisAngle := true; + fi; + endfor; + else: + useThisAngle := true; + fi; + if (useThisAngle): + if (length(modAnglesListNew) = 0): + modAnglesListNew := str j; + else: + modAnglesListNew := str j & ", " & modAnglesListNew; + fi; + fi; + endfor; + modAnglesList := modAnglesListNew; + if (length(modAnglesList)>0): + forever: + anglesToDraw := ""; + forever: + modAnglesListNew := ""; + anglesAdded := 0; + forsuffixes j=scantokens(modAnglesList): + if (length(anglesToDraw) = 0): + anglesToDraw := str j; + else: + forsuffixes k=scantokens(anglesToDraw): + if ((abs(getAttribute("angle", "AVal", str k) - getAttribute("angle", "AVal", str j)) mod 360) < 1) + or ((abs(getAttribute("angle", "CVal", str k) - getAttribute("angle", "AVal", str j)) mod 360) < 1) + or ((abs(getAttribute("angle", "AVal", str k) - getAttribute("angle", "CVal", str j)) mod 360) < 1) + or ((abs(getAttribute("angle", "CVal", str k) - getAttribute("angle", "CVal", str j)) mod 360) < 1): + appendList(anglesToDraw, str j, 1, true); + anglesAdded := anglesAdded + 1; + fi; + endfor; + fi; + endfor; + forsuffixes j=scantokens(modAnglesList): + angleIsAdded := false; + forsuffixes k=scantokens(anglesToDraw): + if ((str j) = (str k)): + angleIsAdded := true; + fi; + endfor; + if not angleIsAdded: + appendList(modAnglesListNew, str j, 1, false); + fi; + endfor; + modAnglesList := modAnglesListNew; + exitif (anglesAdded = 0) or (length(modAnglesList)=0); + endfor; + v := 0; + forsuffixes k=scantokens(anglesToDraw): + %v := v + angleVal.k; + if (getAttribute("angle", "Val", str k) > v): v := getAttribute("angle", "Val", str k); fi; + endfor; + sc := angleOpticalScale(v); + startTempAngleScale(angleScale*sc); + draw byNamedAngle(scantokens(anglesToDraw)); + stopTempAngleScale; + exitif (length(modAnglesList)=0); + endfor; + fi; + endfor; + ) +enddef; + +vardef generateAngleSynonyms = + if string anglePointsList: + save n, candidateName, originalName; + numeric n; + string candidateName[], originalName; + forsuffixes i=scantokens(anglePointsList): + n := 0; + pointAnglesList.i := sortPointAnglesList(pointAnglesList.i); + forsuffixes j=scantokens(pointAnglesList.i): + if (length(str j) > 0): + n := n + 1; + originalName := str j; + candidateName0 := getAttribute("angle", "BName", originalName); + candidateName1 := getAttribute("angle", "AName", originalName) + & getAttribute("angle", "BName", originalName) + & getAttribute("angle", "CName", originalName); + candidateName2 := getAttribute("angle", "CName", originalName) + & getAttribute("angle", "BName", originalName) + & getAttribute("angle", "AName", originalName); + if not string getAttribute("angle", "Synonym", candidateName1): + setAttribute("angle", "SynonymPartial", candidateName1, false); + setAttribute("angle", "Synonym", candidateName1, originalName); + fi; + if not string getAttribute("angle", "Synonym", candidateName2): + setAttribute("angle", "SynonymPartial", candidateName2, false); + setAttribute("angle", "Synonym", candidateName2, originalName); + fi; + fi; + endfor; + if (n = 1): + if not string getAttribute("angle", "Synonym", candidateName0): + setAttribute("angle", "SynonymPartial", candidateName0, false); + setAttribute("angle", "Synonym", candidateName0, originalName); + fi; + fi; + endfor; + fi; +enddef; + +vardef sortPointAnglesList (expr anglesListToSort) = + save preList, postList, nPre, nPost, isSorted, value, firstValue, lastValue, rv; + string preList, postList, rv; + boolean isSorted; + numeric value, firstValue, lastValue, nPre, nPost; + lastValue := -360; + isSorted := true; + preList := ""; + postList := ""; + nPre := 0; + nPost := 0; + forsuffixes i=scantokens(anglesListToSort): + value := getAttribute("angle", "Direction", str i); + if (unknown firstValue): + firstValue := value; + appendList(postList, str i, 0, true); + fi; + if (value < firstValue): + appendList(preList, str i, 0, true); + nPre := nPre + 1; + else: + appendList(postList, str i, 0, true); + nPost := nPost + 1; + fi; + if (lastValue > value): + isSorted := false; + fi; + lastValue := value; + endfor; + if (isSorted): + rv := anglesListToSort; + else: + if (nPre > 1): + preList := sortPointAnglesList(preList); + fi; + if (nPre > 0): + preList := preList & ", "; + fi; + if (nPost > 1): + postList := sortPointAnglesList(postList); + fi; + rv := preList & postList; + fi; + rv +enddef; + +vardef bySplitStringIntoAngles (expr angleName) = + save pointA, pointB, pointC, cPointA, cPointB, cPointC, n; + string pointA, pointB, pointC, cPointA, cPointB, cPointC, rv; + numeric n; + n := 0; + for i := 1 step 1 until (length(angleName) - 2): + for j := i + 1 step 1 until (length(angleName) - 1): + cPointA := substring (0, i) of angleName; + cPointB := substring (i, j) of angleName; + cPointC := substring (j, length(angleName)) of angleName; + if known scantokens(cPointA): + if known scantokens(cPointB): + if known scantokens(cPointC): + n := n + 1; + pointA := cPointA; + pointB := cPointB; + pointC := cPointC; + fi; + fi; + fi; + endfor; + endfor; + if (known scantokens(angleName)) and (n = 0): + rv := ""; + else: + if n = 0: + errmessage("Can't find points for angle: " & lineName); + fi; + if n > 1: + errmessage("Ambiguous angle name: " & lineName); + fi; + rv := pointA & ", " & pointB & ", " & pointC; + fi; + rv +enddef; + +vardef byConstructCompoundAngle (suffix pointA, pointB, pointC) = + save angleList, isFirst, isLast, cwA, cwC, cwAName, cwCName, dirA, dirC, rv, threeDMode, notInPlane; + string cwAName, cwCName, angleList, rv; + boolean isFirst, isLast, threeDMode, notInPlane; + pair cwA, cwC, dirA, dirC; + angleList := ""; + dirA := pointA - pointB; + dirC := pointC - pointB; + threeDMode := false; + if byIsPointInSpace(pointA, pointB, pointC): + threeDMode := true; + fi; + isFirst := false; + isLast := false; + forsuffixes i=scantokens(pointAnglesList.pointB & ", " & pointAnglesList.pointB): + if byIsArcClockwise(byConstructAngleArc( + scantokens(getAttribute("angle", "AName", str i)), + scantokens(getAttribute("angle", "BName", str i)), + scantokens(getAttribute("angle", "CName", str i)), + getAttribute("angle", "ArcType", str i))): + cwAName := getAttribute("angle", "CName", str i); + cwCName := getAttribute("angle", "AName", str i); + else: + cwAName := getAttribute("angle", "AName", str i); + cwCName := getAttribute("angle", "CName", str i); + fi; + cwA := scantokens(cwAName) - scantokens(getAttribute("angle", "BName", str i)); + cwC := scantokens(cwCName) - scantokens(getAttribute("angle", "BName", str i)); + notInPlane := false; + if threeDMode: + if (not isInPlane(pointXYZ.scantokens(cwAName), + pointXYZ.pointA, + pointXYZ.pointB, + pointXYZ.pointC)) + or (not isInPlane(pointXYZ.scantokens(cwCName), + pointXYZ.pointA, + pointXYZ.pointB, + pointXYZ.pointC)): + notInPlane := true; + fi; + fi; + if not notInPlane: + if ((abs(angle(dirA) - angle(cwA)) mod 360) < 1) and (not isFirst): + isFirst := true; + fi; + if isFirst and not isLast: + appendList(angleList, str i, 1, true) + fi; + if ((abs(angle(dirC) - angle(cwC)) mod 360) < 1) and isFirst: + isLast := true; + fi; + fi; + endfor; + if (not isFirst) or (not isLast): + errmessage("Can't construct angle: " & str pointA & str pointB & str pointC); + fi; + rv := angleList; + rv +enddef; + +vardef twoRightAngles = + save textLabels; + boolean textLabels; + textLabels := false; + image( + draw byNamedAngle(rightAngleWN,rightAngleNE); + draw byNamedAngleDummySides(rightAngleWN,rightAngleNE); + ) +enddef; + +vardef rightAngle = + save textLabels; + boolean textLabels; + textLabels := false; + image( + draw byNamedAngle(rightAngleWN); + draw byNamedAngleDummySides(rightAngleWN); + ) +enddef; + +% Angles we often need +pair pointN, pointE, pointS, pointW, pointO; +pointN := (0, 2angleSize*angleScale); +pointE := (2angleSize*angleScale, 0); +pointS := (0, -2angleSize*angleScale); +pointW := (-2angleSize*angleScale, 0); +pointO := (0, 0); + +vardef byDefineGenericRightAngles(expr angleInsteadOfArc) = + byAngleDefine.rightAngleNE (pointN, pointO, pointE)(black, 1); + byAngleDefine.rightAngleES (pointE, pointO, pointS)(black, 1); + byAngleDefine.rightAngleSW (pointS, pointO, pointW)(black, 1); + byAngleDefine.rightAngleWN (pointW, pointO, pointN)(black, 1); + if (angleInsteadOfArc): + byConsiderAngleRight(rightAngleNE); + byConsiderAngleRight(rightAngleES); + byConsiderAngleRight(rightAngleSW); + byConsiderAngleRight(rightAngleWN); + fi; +enddef; + +byDefineGenericRightAngles(false); + +% +% Arrows +% + +def commonArrowSettings = + save ahlength, ahangle; + ahlength := 7pt; + ahangle := 30; +enddef; + +vardef byRotationArrowDefine@#(suffix axisA, axisB, pointA) (expr ang, col, sty) = +if str @# = "": + byRotationArrowDefine.scantokens(str axisA & str axisB & str pointA)(axisA, axisB, pointA) (ang, col, sty); +else: + setAttribute("rotationarrow", "AxisA", str @#, axisA); + setAttribute("rotationarrow", "AxisB", str @#, axisB); + setAttribute("rotationarrow", "Point", str @#, pointA); + setAttribute("rotationarrow", "AxisAName", str @#, str axisA); + setAttribute("rotationarrow", "AxisBName", str @#, str axisB); + setAttribute("rotationarrow", "PointName", str @#, str pointA); + setAttribute("rotationarrow", "Angle", str @#, ang); + setAttribute("rotationarrow", "Color", str @#, col); + setAttribute("rotationarrow", "Style", str @#, sty); + setAttribute("rotationarrow", "Synonym", str @#, str @#); +fi; +enddef; + +vardef byGenerateRotationArrowPath (expr axisA, axisB, pointA, ang) = + save arrowPath, spacePoint; + path arrowPath; + color spacePoint; + spacePoint := pointA colorrotated projectionAngle; + arrowPath := (redpart(spacePoint), greenpart(spacePoint)); + for i := 0 step (ang/floor(ang/5)) until ang: + spacePoint := byRotateAroundAxis (axisA, axisB, i, pointA) colorrotated projectionAngle; + arrowPath := arrowPath -- (redpart(spacePoint), greenpart(spacePoint)); + endfor; + arrowPath +enddef; + +vardef byNamedRotationArrow (text arrowsList) = + save arrowPath, arrowPoint, arrowStyle; + path arrowPath; + color arrowPoint; + numeric arrowStyle; + commonArrowSettings; + image( + forsuffixes aN=arrowsList: + arrowPath := byGenerateRotationArrowPath( + pointXYZ.scantokens(getAttribute("rotationarrow", "AxisAName", str aN)), + pointXYZ.scantokens(getAttribute("rotationarrow", "AxisBName", str aN)), + pointXYZ.scantokens(getAttribute("rotationarrow", "PointName", str aN)), + getAttribute("rotationarrow", "Angle", str aN) + ) scaled scaleFactor; + arrowStyle := getAttribute("rotationarrow", "Style", str aN); + if arrowStyle = 0: + arrowPath := subpath (0, (arctime (arclength(arrowPath) - ahlength) of arrowPath)) of arrowPath; + drawarrow arrowPath withpen (pencircle scaled lineWidthThin) withcolor getAttribute("rotationarrow", "Color", str aN); + else: + arrowPath := subpath ((arctime ahlength of arrowPath), (arctime (arclength(arrowPath) - ahlength) of arrowPath)) of arrowPath; + drawdblarrow arrowPath withpen (pencircle scaled lineWidthThin) withcolor getAttribute("rotationarrow", "Color", str aN); + fi; + endfor; + ) rotated globalRotation +enddef; + +vardef byFlatArrowDefine@#(suffix pointA, pointB) (expr col, sty, typ) = +if str @# = "": + byFlatArrowDefine.scantokens(str pointA & str pointB)(pointA, pointB) (col, sty, typ); +else: + setAttribute("flatarrow", "PointA", str @#, pointA); + setAttribute("flatarrow", "PointB", str @#, pointB); + setAttribute("flatarrow", "PointAName", str @#, str pointA); + setAttribute("flatarrow", "PointBName", str @#, str pointB); + setAttribute("flatarrow", "Type", str @#, typ); + setAttribute("flatarrow", "Color", str @#, col); + setAttribute("flatarrow", "Style", str @#, sty); + setAttribute("flatarrow", "Synonym", str @#, str @#); +fi; +enddef; + +vardef byNamedFlatArrow (text arrowsList) = + save arrowPath, arrowStyle, arrowPoint; + path arrowPath; + numeric arrowStyle; + pair arrowPoint[]; + commonArrowSettings; + image( + forsuffixes aN=arrowsList: + arrowType := getAttribute("flatarrow", "Type", str aN); + arrowStyle := getAttribute("flatarrow", "Style", str aN); + arrowPoint1 := scantokens(getAttribute("flatarrow", "PointAName", str aN)); + arrowPoint2 := scantokens(getAttribute("flatarrow", "PointBName", str aN)); + if arrowType = 0: + arrowPath := arrowPoint1 -- arrowPoint2; + elseif arrowType = 1: + arrowPath := arrowPoint1 + .. 1/2[arrowPoint1, arrowPoint2] + shifted (((arrowPoint2 - arrowPoint1) scaled 1/8) rotated 90) + .. arrowPoint2; + fi; + arrowPath := arrowPath scaled scaleFactor; + if arrowStyle = 0: + arrowPath := subpath ( + 0, + arctime (arclength(arrowPath) - ahlength) of arrowPath + ) of arrowPath; + drawarrow arrowPath withpen (pencircle scaled lineWidthThin) withcolor getAttribute("flatarrow", "Color", str aN); + else: + arrowPath := subpath ( + arctime ahlength of arrowPath, + arctime (arclength(arrowPath) - ahlength) of arrowPath + ) of arrowPath; + drawdblarrow arrowPath withpen (pencircle scaled lineWidthThin) withcolor getAttribute("flatarrow", "Color", str aN); + fi; + endfor; + ) rotated globalRotation +enddef; + +% +% Spheres +% + +%vardef bySphereDefine@#(expr ox, oy, oz, r) = +%if str @# = "": +% errmessage("bySphereDefine needs a name (bySphereDefine.somename...)"); +%else: +% if mainPictureMode: +% appendList(allSpheresList, str @#, 1, true); +% fi; +% setAttribute("sphere", "XYZ", str @#, (ox, oy, oz)); +% setAttribute("sphere", "radius", str @#, r); +%fi; +%enddef; +% +%vardef byNamedSphere (test spheresList) = +% image( +% forsuffixes cN=spheresList: +% endfor; +% ) +%enddef; +% +%vardef byFindSpheresIntersection (text spheresList) = +%enddef; + +% +% Magnitudes +% + +% along with 'magnitudesymbol' one symbol long 'magnitude' is also defined at the same time. +vardef byMagnitudeSymbolDefine@#(expr shp, col, sty) = +if str @# = "": + byMagnitudeSymbolDefine.scantokens(shp)(shp, col, sty); +else: + setAttribute("magnitude", "Color", str @#, col); + setAttribute("magnitude", "Shape", str @#, shp); + setAttribute("magnitude", "Style", str @#, sty); + setAttribute1("magnitude", "Symbol", str @#, str @#); + setAttribute1("magnitude", "N", str @#, 1); + setAttribute("magnitude", "Alignment", str @#, 0); + setAttribute("magnitude", "NumberOfRows", str @#, 1); + setAttribute("magnitude", "Horizontal", str @#, false); +fi; +enddef; + +vardef byNamedMagnitudeSymbol (expr n, hor)(suffix magnitudeSymbolName) = + save p, q, i, s, magnitudeSizeCor; + path p; + pair s[]; + picture q; + numeric magnitudeSizeCor; + p := (0, 0); + magnitudeSizeCor := 1; + if (getAttribute("magnitude", "Shape", str magnitudeSymbolName) = "circle"): + p := fullcircle scaled (magnitudeScale*magnitudeSize); + magnitudeSizeCor := 8/7; + elseif (getAttribute("magnitude", "Shape", str magnitudeSymbolName) = "semicircleUp"): + p := ((subpath (0, 4) of fullcircle) -- (-1/2, -1/2) -- (1/2, -1/2) -- cycle) scaled (magnitudeScale*magnitudeSize); + elseif (getAttribute("magnitude", "Shape", str magnitudeSymbolName) = "semicircleDown"): + p := (((subpath (0, 4) of fullcircle) -- (-1/2, -1/2) -- (1/2, -1/2) -- cycle) yscaled -1) scaled (magnitudeScale*magnitudeSize); + elseif (getAttribute("magnitude", "Shape", str magnitudeSymbolName) = "sectorDown"): + p := ((subpath (0, 4) of fullcircle) -- (0, -1/2) -- cycle) scaled (magnitudeScale*magnitudeSize); + magnitudeSizeCor := 8/7; + elseif (getAttribute("magnitude", "Shape", str magnitudeSymbolName) = "sectorUp"): + p := ((subpath (0, 4) of fullcircle) -- (0, -1/2) -- cycle) scaled (magnitudeScale*magnitudeSize) yscaled -1; + magnitudeSizeCor := 8/7; + elseif (getAttribute("magnitude", "Shape", str magnitudeSymbolName) = "wedgeUp"): + p := (((1/2, 0) -- (1/2, 1/2) -- (-1/2, 1/2) -- (-1/2, 0) -- (0, -1/2) -- cycle) yscaled -1) scaled (magnitudeScale*magnitudeSize); + elseif (getAttribute("magnitude", "Shape", str magnitudeSymbolName) = "wedgeDown"): + p := ((1/2, 0) -- (1/2, 1/2) -- (-1/2, 1/2) -- (-1/2, 0) -- (0, -1/2) -- cycle) scaled (magnitudeScale*magnitudeSize); + elseif (getAttribute("magnitude", "Shape", str magnitudeSymbolName) = "square"): + p := ((1/2, 1/2) -- (-1/2, 1/2) -- (-1/2, -1/2) -- (1/2, -1/2) -- cycle) scaled (magnitudeScale*magnitudeSize); + elseif (getAttribute("magnitude", "Shape", str magnitudeSymbolName) = "halfsquare"): + p := ((1/2, 1/2) -- (-1/2, 1/2) -- (-1/2, 0) -- (1/2, 0) -- cycle) scaled (magnitudeScale*magnitudeSize); + elseif (getAttribute("magnitude", "Shape", str magnitudeSymbolName) = "rhombus"): + p := ((0, 1/2) -- (-1/2, 0) -- (0, -1/2) -- (1/2, 0) -- cycle) scaled (magnitudeScale*magnitudeSize); + magnitudeSizeCor := 8/7; + elseif (getAttribute("magnitude", "Shape", str magnitudeSymbolName) = "halfrhombusUp"): + p := ((0, 1/2) -- (-1/2, 0) -- (1/2, 0) -- cycle) scaled (magnitudeScale*magnitudeSize); + magnitudeSizeCor := 8/7; + elseif (getAttribute("magnitude", "Shape", str magnitudeSymbolName) = "miniTriangleUp"): + p := ((-1/4, -1/4) -- (1/4, -1/4) -- (0, 1/4) -- cycle) scaled (magnitudeScale*magnitudeSize); + magnitudeSizeCor := 8/7; + elseif (getAttribute("magnitude", "Shape", str magnitudeSymbolName) = "miniTriangleDown"): + p := ((-1/4, 1/4) -- (1/4, 1/4) -- (0, -1/4) -- cycle) scaled (magnitudeScale*magnitudeSize); + magnitudeSizeCor := 8/7; + elseif (getAttribute("magnitude", "Shape", str magnitudeSymbolName) = "miniSquare"): + p := ((-1/4, -1/4) -- (1/4, -1/4) -- (1/4, 1/4) -- (-1/4, 1/4) -- cycle) scaled (magnitudeScale*magnitudeSize); + elseif (getAttribute("magnitude", "Shape", str magnitudeSymbolName) = "miniCircle"): + p := fullcircle scaled (1/2magnitudeScale*magnitudeSize); + magnitudeSizeCor := 8/7; + fi; + if (hor): + s1 := (0, ypart(urcorner(p)) - ypart(lrcorner(p))); + s2 := (0, magnitudeGap); + else: + s1 := (xpart(urcorner(p)) - xpart(ulcorner(p)), 0); + s2 := (magnitudeGap, 0); + fi; + p := p scaled magnitudeSizeCor; + q := image( + draw p withpen (pencircle scaled lineWidthThin) withcolor getAttribute("magnitude", "Color", str magnitudeSymbolName); + if (getAttribute("magnitude", "Style", str magnitudeSymbolName) = 0): + fill p withcolor getAttribute("magnitude", "Color", str magnitudeSymbolName); + fi; + ); + image( + for i := 1 step 1 until n: + draw q shifted (s1 scaled i) shifted (s2 scaled i); + endfor; + ) +enddef; + +vardef byMagnitudeDefine@#(expr al, hor)(text rowsList)(text magnitudeSymbolsList) = +if str @# = "": + errmessage("byMagnitudeDefine needs a name (byMagnitudeDefine.somename...)"); +else: + setAttribute("magnitude", "Horizontal", str @#, hor); + setAttribute("magnitude", "Alignment", str @#, al); + save i; + numeric i; + i := 0; + forsuffixes rN=rowsList: + i := i + 1; + setAttribute[i]("magnitude", "N", str @#, rN); + endfor; + setAttribute("magnitude", "NumberOfRows", str @#, i); + i := 0; + forever: + forsuffixes sN=magnitudeSymbolsList: + i := i + 1; + if (i <= getAttribute("magnitude", "NumberOfRows", str @#)): + setAttribute[i]("magnitude", "Symbol", str @#, str sN); + fi; + endfor; + exitif (i >= getAttribute("magnitude", "NumberOfRows", str @#)); + endfor; +fi; +enddef; + +vardef byNamedMagnitude (expr excl) (suffix magnitudeName) = + save p, s, h, magC; + picture p; + pair s[]; + numeric h; + h := 0; + image( + for magC := 1 step 1 until getAttribute("magnitude", "NumberOfRows", str magnitudeName): + if (excl = 0) or ((excl < 0) and (magC <> -excl)) or ((excl > 0) and (magC = excl)): + p := byNamedMagnitudeSymbol(getAttribute[magC]("magnitude", "N", str magnitudeName), getAttribute("magnitude", "Horizontal", str magnitudeName))(scantokens(getAttribute[magC]("magnitude", "Symbol", str magnitudeName))); + if (getAttribute("magnitude", "Horizontal", str magnitudeName)): + if (getAttribute("magnitude", "Alignment", str magnitudeName) = 0): + s1 := 1/2[urcorner(p),lrcorner(p)]; + elseif (getAttribute("magnitude", "Alignment", str magnitudeName) = 1): + s1 := lrcorner(p); + elseif (getAttribute("magnitude", "Alignment", str magnitudeName) = -1): + s1 := urcorner(p); + fi; + s2 := (h, 0); + else: + if (getAttribute("magnitude", "Alignment", str magnitudeName) = 0): + s1 := 1/2[ulcorner(p),urcorner(p)]; + elseif (getAttribute("magnitude", "Alignment", str magnitudeName) = 1): + s1 := urcorner(p); + elseif (getAttribute("magnitude", "Alignment", str magnitudeName) = -1): + s1 := ulcorner(p); + fi; + s2 := (0, -h); + fi; + draw p shifted -s1 shifted s2; + if (getAttribute("magnitude", "Horizontal", str magnitudeName)): + h := h + (abs(ulcorner(p)-urcorner(p))) + magnitudeGap; + else: + h := h + (abs(ulcorner(p)-llcorner(p))) + magnitudeGap; + fi; + fi; + endfor; + ) +enddef; + +% +% Text labels +% + +% If using metafun, use textext + +if known metafunversion: + vardef textLabelRender(expr t) = + textext("\textsf{" & t & "}") scaled 2/5 + enddef; +else: + input TEX; + TEXPRE("%&latex" & char(10) & "\documentclass{article}\usepackage[utf8]{inputenc}\usepackage[russian]{babel}\begin{document}"); + TEXPOST("\end{document}"); + vardef textLabelRender(expr t) = + TEX("\textsf{" & t & "}") scaled 2/5 + enddef; +fi; + +numeric textLabelAvSize; +textLabelAvSize := abs(ulcorner(textLabelRender("A")) - llcorner(textLabelRender("A"))); + +vardef byTextLabel(suffix labelType)(expr t, p, a, d) = + save labelItself, labelString, bb; + path bb; + picture labelItself; + string labelString; + if not string labelType.scantokens(t): + string labelType.scantokens(t); + labelType.scantokens(t) := t; + fi; + labelString := str labelType & "." & t; + if (not isInList(labelString, uniqueTextLabels)) or (not omitDuplicateTextLabels): + appendList(uniqueTextLabels, labelString, 1, false); + labelItself := textLabelRender(labelType.scantokens(t)); + labelItself := labelItself shifted -1/2[ulcorner(labelItself), lrcorner(labelItself)]; + if (d > 0): + bb := ulcorner(labelItself) -- urcorner(labelItself) -- lrcorner(labelItself) -- llcorner(labelItself)--cycle; + labelItself := labelItself shifted -(bb intersectionpoint ((0,0)--(dir(a+180+globalRotation)*1cm))); + fi; + labelItself := image( + draw labelItself shifted ((p scaled scaleFactor) rotated globalRotation) shifted (dir(a+globalRotation)*d); + ); + else: + labelItself := image(); + fi; + labelItself +enddef; + +vardef byLabelPoint(suffix p)(expr a, d) = + image( + if (textLabels): + draw byTextLabel(pointLabel)(str p, p, a, textLabelShift*d); + fi; + ) +enddef; + +vardef byLabelLine(expr d)(text linesList) = + save a; + numeric a; + if (d = 0): + a := 90; + else: + a := -90; + fi; + image( + if (textLabels): + forsuffixes lN=linesList: + if (getAttribute("line", "UseLineLabel", str lN)): + draw byTextLabel(pointLabel)(getAttribute("line", "Label", str lN), + 1/2[scantokens(getAttribute("line", "EndAName", str lN)), + scantokens(getAttribute("line", "EndBName", str lN))], + getAttribute("line", "Angle", str lN) + a, textLabelShift); + else: + draw byTextLabel(pointLabel)( + getAttribute("line", "EndAName", str lN), + scantokens(getAttribute("line", "EndAName", str lN)), + getAttribute("line", "Angle", str lN) + a, + textLabelShift); + draw byTextLabel(pointLabel)( + getAttribute("line", "EndBName", str lN), + scantokens(getAttribute("line", "EndBName", str lN)), + getAttribute("line", "Angle", str lN) + a, + textLabelShift); + fi; + endfor; + fi; + ) +enddef; + +vardef byLabelPolygon(expr d)(text polygonsList) = + image( + if (textLabels): + draw byLabelsOnPolygon(scantokens byMergePolygons(polygonsList))(1, d); + fi; + ) +enddef; + +vardef byLabelCircle(expr l, cn) = + save o, d; + pair o; + numeric d; + o := getAttribute("circle", "Center", cn); + d := lineThickness(getAttribute("circle", "Thin", cn))*(1/2-1/2getAttribute("circle", "Shift", cn)); + byTextLabel(pointLabel)(l, scantokens(l), angle(o - scantokens(l)), textLabelShift + d) +enddef; + +vardef byLabelsOnCircle(text pointsList)(suffix cn) = + save d, t, s, c; + numeric t, s; + pair c; + if string getAttribute("circle", "CenterName", str cn): + t := getAttribute("circle", "Thin", str cn); + s := getAttribute("circle", "Shift", str cn); + c := byReturnCircleCenter(str cn); + elseif string getAttribute("arc", "CenterName", str cn): + t := getAttribute("arc", "Thin", str cn); + s := getAttribute("arc", "Shift", str cn); + c := scantokens(getAttribute("arc", "CenterName", str cn)); + else: + errmessage("There is no circle or arc named " & str cn); + fi; + d := lineThickness(t)*(1/2+1/2s); + image( + if (textLabels): + forsuffixes pN=pointsList: + draw byTextLabel(pointLabel)(str pN, pN, angle(pN - c), textLabelShift + d); + endfor; + fi; + ) +enddef; + +vardef byLabelsOnPolygon(text pointsList)(expr sty, shft)= + image( + if(textLabels): + save pointName, pointLoc, i, j, k, l, p, q, stump, sv; + numeric i, j, k, l, stump, sv; + string pointName[]; + pair pointLoc[], p[]; + i := -1; + forsuffixes pN=pointsList: + boolean pointDrawn.pN; + pointDrawn.pN := false; + i := i + 1; + pointName[i] := str pN; + if (pointName[i] <> "noPoint"): + pointLoc[i] := pN; + fi; + if (i > 0): + if (pointName[i] = pointName[i-1]): + i := i - 1; + fi; + fi; + endfor; + if (pointName[i] = pointName[0]): + i := i - 1; + fi; + if (i = 1): + i := i + 1; + pointName[i] := "noPoint"; + fi; + for j := 0 step 1 until i: + k := cycleval(j - 1, i + 1); + l := cycleval(j + 1, i + 1); + if (pointName[j] <> "noPoint"): + p1 := pointLoc[j]; + stump := 0; + if (pointName[k] <> "noPoint") or (pointName[l] <> "noPoint"): + if (pointName[k] = "noPoint"): + p2 := pointLoc[l]; + p0 := 2[p2, p1]; + stump := -1; + elseif (pointName[l] = "noPoint"): + p0 := pointLoc[k]; + p2 := 2[p0, p1]; + stump := +1; + else: + p0 := pointLoc[k]; + p2 := pointLoc[l]; + fi; + fi; + pair q[]; + q4 := unitvector(p1-p0) rotated 90; + q5 := unitvector(p2-p1) rotated 90; + if ((abs(angle(q4) - angle(q5)) mod 180) > 1): + q0 := p0 shifted q4; + q1 := p1 shifted q4; + q2 := p1 shifted q5; + q3 := p2 shifted q5; + q6 = whatever[q0, q1] = whatever[q2, q3]; + else: + if (sty <> 1) or (stump <> 0): + q6 := p1 shifted q4; + else: + q6 := p1; + fi; + fi; + if (sty = 2) and ((j = 0) or (j = i)): + pointDrawn.scantokens(pointName[j]) := true; + fi; + if (sty = 3) and (j = 0): + pointDrawn.scantokens(pointName[j]) := true; + fi; + if (sty = 4) and (j = i): + pointDrawn.scantokens(pointName[j]) := true; + fi; + if (q6 <> p1) and (not pointDrawn.scantokens(pointName[j])): + sv := byLabelAngleCompensate(p0, p1, p2, shft); + draw byTextLabel(pointLabel)(pointName[j], pointLoc[j], angle(q6-p1), textLabelShift+sv); + pointDrawn.scantokens(pointName[j]):= true; + fi; + fi; + endfor; + fi; + ) +enddef; + +vardef byLabelLineEnd (suffix a, b)(expr d) = + image( + if (textLabels): + draw byTextLabel(pointLabel)(str a, a, angle(a-b), textLabelShift+(d*lineWidth)); + fi; + ) +enddef; + +vardef byLabelAngleCompensate (expr a, b, c, s) = + save ang, ins; + numeric ang, ins; + ang := angleValue(a, b, c)/2; + if (sind(ang*2)>0): + ins := (cosd(ang)/(2*sind(ang)))*textLabelAvSize; + else: + ins := 0; + fi; + (((abs(sind(ang)) + abs(cosd(ang)/abs(sind(ang))))*(lineWidth*(1/2+1/2s)))/2) + ins +enddef; |