shapes.c 120 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357
  1. /// @file
  2. /// @brief [node shapes](https://graphviz.org/doc/info/shapes.html)
  3. /// @ingroup common_render
  4. /*************************************************************************
  5. * Copyright (c) 2011 AT&T Intellectual Property
  6. * All rights reserved. This program and the accompanying materials
  7. * are made available under the terms of the Eclipse Public License v1.0
  8. * which accompanies this distribution, and is available at
  9. * https://www.eclipse.org/legal/epl-v10.html
  10. *
  11. * Contributors: Details at https://graphviz.org
  12. *************************************************************************/
  13. #include <assert.h>
  14. #include <cgraph/gv_math.h>
  15. #include <common/render.h>
  16. #include <common/htmltable.h>
  17. #include <float.h>
  18. #include <limits.h>
  19. #include <math.h>
  20. #include <stddef.h>
  21. #include <stdbool.h>
  22. #include <string.h>
  23. #include <util/alloc.h>
  24. #include <util/streq.h>
  25. #include <util/unreachable.h>
  26. #define RBCONST 12
  27. #define RBCURVE .5
  28. typedef struct {
  29. pointf (*size_gen) (pointf);
  30. void (*vertex_gen) (pointf*, pointf*);
  31. } poly_desc_t;
  32. static port Center = {.theta = -1, .clip = true};
  33. #define ATTR_SET(a,n) ((a) && (*(agxget(n,a->index)) != '\0'))
  34. /* Default point size = 0.05 inches or 3.6 points */
  35. #define DEF_POINT 0.05
  36. /* Minimum point size = 0.0003 inches or 0.02 points
  37. * This will make the radius 0.01 points, which is the smallest
  38. * non-zero number output by gvprintdouble in gvdevice.c
  39. */
  40. #define MIN_POINT 0.0003
  41. /* extra null character needed to avoid style emitter from thinking
  42. * there are arguments.
  43. */
  44. static char *point_style[3] = { "invis\0", "filled\0", 0 };
  45. /* forward declarations of functions used in shapes tables */
  46. static void poly_init(node_t * n);
  47. static void poly_free(node_t * n);
  48. static port poly_port(node_t * n, char *portname, char *);
  49. static bool poly_inside(inside_t * inside_context, pointf p);
  50. static int poly_path(node_t * n, port * p, int side, boxf rv[], int *kptr);
  51. static void poly_gencode(GVJ_t * job, node_t * n);
  52. static void record_init(node_t * n);
  53. static void record_free(node_t * n);
  54. static port record_port(node_t * n, char *portname, char *);
  55. static bool record_inside(inside_t * inside_context, pointf p);
  56. static int record_path(node_t * n, port * p, int side, boxf rv[],
  57. int *kptr);
  58. static void record_gencode(GVJ_t * job, node_t * n);
  59. static void point_init(node_t * n);
  60. static void point_gencode(GVJ_t * job, node_t * n);
  61. static bool point_inside(inside_t * inside_context, pointf p);
  62. static bool epsf_inside(inside_t * inside_context, pointf p);
  63. static void epsf_gencode(GVJ_t * job, node_t * n);
  64. static pointf star_size (pointf);
  65. static void star_vertices (pointf*, pointf*);
  66. static bool star_inside(inside_t * inside_context, pointf p);
  67. static poly_desc_t star_gen = {
  68. star_size,
  69. star_vertices,
  70. };
  71. static pointf cylinder_size (pointf);
  72. static void cylinder_vertices (pointf*, pointf*);
  73. static void cylinder_draw(GVJ_t *job, pointf *AF, size_t sides, int filled);
  74. static poly_desc_t cylinder_gen = {
  75. cylinder_size,
  76. cylinder_vertices,
  77. };
  78. /* polygon descriptions. "polygon" with 0 sides takes all user control */
  79. /* regul perip sides orien disto skew */
  80. static polygon_t p_polygon = {.peripheries = 1};
  81. /* builtin polygon descriptions */
  82. static polygon_t p_ellipse = {.peripheries = 1, .sides = 1};
  83. static polygon_t p_circle = {.regular = true, .peripheries = 1, .sides = 1};
  84. static polygon_t p_egg = {.peripheries = 1, .sides = 1, .distortion = -0.3};
  85. static polygon_t p_triangle = {.peripheries = 1, .sides = 3};
  86. static polygon_t p_box = {.peripheries = 1, .sides = 4};
  87. static polygon_t p_square = {.regular = true, .peripheries = 1, .sides = 4};
  88. static polygon_t p_plaintext = {.sides = 4};
  89. static polygon_t p_plain = {.sides = 4};
  90. static polygon_t p_diamond = {.peripheries = 1, .sides = 4, .orientation = 45.0};
  91. static polygon_t p_trapezium = {.peripheries = 1, .sides = 4, .distortion = -0.4};
  92. static polygon_t p_parallelogram = {.peripheries = 1, .sides = 4, .skew = 0.6};
  93. static polygon_t p_house = {.peripheries = 1, .sides = 5, .distortion = -0.64};
  94. static polygon_t p_pentagon = {.peripheries = 1, .sides = 5};
  95. static polygon_t p_hexagon = {.peripheries = 1, .sides = 6};
  96. static polygon_t p_septagon = {.peripheries = 1, .sides = 7};
  97. static polygon_t p_octagon = {.peripheries = 1, .sides = 8};
  98. static polygon_t p_note = {
  99. .peripheries = 1, .sides = 4, .option = {.shape = DOGEAR}};
  100. static polygon_t p_tab = {
  101. .peripheries = 1, .sides = 4, .option = {.shape = TAB}};
  102. static polygon_t p_folder = {
  103. .peripheries = 1, .sides = 4, .option = {.shape = FOLDER}};
  104. static polygon_t p_box3d = {
  105. .peripheries = 1, .sides = 4, .option = {.shape = BOX3D}};
  106. static polygon_t p_component = {
  107. .peripheries = 1, .sides = 4, .option = {.shape = COMPONENT}};
  108. static polygon_t p_underline = {
  109. .peripheries = 1, .sides = 4, .option = {.underline = true}};
  110. static polygon_t p_cylinder = {.peripheries = 1,
  111. .sides = 19,
  112. .option = {.shape = CYLINDER},
  113. .vertices = (pointf *)&cylinder_gen};
  114. /* redundant and undocumented builtin polygons */
  115. static polygon_t p_doublecircle = {
  116. .regular = true, .peripheries = 2, .sides = 1};
  117. static polygon_t p_invtriangle = {
  118. .peripheries = 1, .sides = 3, .orientation = 180.0};
  119. static polygon_t p_invtrapezium = {
  120. .peripheries = 1, .sides = 4, .orientation = 180.0, .distortion = -0.4};
  121. static polygon_t p_invhouse = {
  122. .peripheries = 1, .sides = 5, .orientation = 180.0, .distortion = -0.64};
  123. static polygon_t p_doubleoctagon = {.peripheries = 2, .sides = 8};
  124. static polygon_t p_tripleoctagon = {.peripheries = 3, .sides = 8};
  125. static polygon_t p_Mdiamond = {
  126. .peripheries = 1,
  127. .sides = 4,
  128. .orientation = 45.0,
  129. .option = {.diagonals = true, .auxlabels = true}};
  130. static polygon_t p_Msquare = {.regular = true,
  131. .peripheries = 1,
  132. .sides = 4,
  133. .option = {.diagonals = true}};
  134. static polygon_t p_Mcircle = {.regular = true,
  135. .peripheries = 1,
  136. .sides = 1,
  137. .option = {.diagonals = true, .auxlabels = true}};
  138. /* non-convex polygons */
  139. static polygon_t p_star = {
  140. .peripheries = 1, .sides = 10, .vertices = (pointf *)&star_gen};
  141. /* biological circuit shapes, as specified by SBOLv*/
  142. /** gene expression symbols **/
  143. static polygon_t p_promoter = {
  144. .peripheries = 1, .sides = 4, .option = {.shape = PROMOTER}};
  145. static polygon_t p_cds = {
  146. .peripheries = 1, .sides = 4, .option = {.shape = CDS}};
  147. static polygon_t p_terminator = {
  148. .peripheries = 1, .sides = 4, .option = {.shape = TERMINATOR}};
  149. static polygon_t p_utr = {
  150. .peripheries = 1, .sides = 4, .option = {.shape = UTR}};
  151. static polygon_t p_insulator = {
  152. .peripheries = 1, .sides = 4, .option = {.shape = INSULATOR}};
  153. static polygon_t p_ribosite = {
  154. .peripheries = 1, .sides = 4, .option = {.shape = RIBOSITE}};
  155. static polygon_t p_rnastab = {
  156. .peripheries = 1, .sides = 4, .option = {.shape = RNASTAB}};
  157. static polygon_t p_proteasesite = {
  158. .peripheries = 1, .sides = 4, .option = {.shape = PROTEASESITE}};
  159. static polygon_t p_proteinstab = {
  160. .peripheries = 1, .sides = 4, .option = {.shape = PROTEINSTAB}};
  161. /** dna construction symbols **/
  162. static polygon_t p_primersite = {
  163. .peripheries = 1, .sides = 4, .option = {.shape = PRIMERSITE}};
  164. static polygon_t p_restrictionsite = {
  165. .peripheries = 1, .sides = 4, .option = {.shape = RESTRICTIONSITE}};
  166. static polygon_t p_fivepoverhang = {
  167. .peripheries = 1, .sides = 4, .option = {.shape = FIVEPOVERHANG}};
  168. static polygon_t p_threepoverhang = {
  169. .peripheries = 1, .sides = 4, .option = {.shape = THREEPOVERHANG}};
  170. static polygon_t p_noverhang = {
  171. .peripheries = 1, .sides = 4, .option = {.shape = NOVERHANG}};
  172. static polygon_t p_assembly = {
  173. .peripheries = 1, .sides = 4, .option = {.shape = ASSEMBLY}};
  174. static polygon_t p_signature = {
  175. .peripheries = 1, .sides = 4, .option = {.shape = SIGNATURE}};
  176. static polygon_t p_rpromoter = {
  177. .peripheries = 1, .sides = 4, .option = {.shape = RPROMOTER}};
  178. static polygon_t p_rarrow = {
  179. .peripheries = 1, .sides = 4, .option = {.shape = RARROW}};
  180. static polygon_t p_larrow = {
  181. .peripheries = 1, .sides = 4, .option = {.shape = LARROW}};
  182. static polygon_t p_lpromoter = {
  183. .peripheries = 1, .sides = 4, .option = {.shape = LPROMOTER}};
  184. static bool IS_BOX(node_t *n) {
  185. return ND_shape(n)->polygon == &p_box;
  186. }
  187. static bool IS_PLAIN(node_t *n) {
  188. return ND_shape(n)->polygon == &p_plain;
  189. }
  190. /// True if style requires processing through round_corners.
  191. static bool SPECIAL_CORNERS(graphviz_polygon_style_t style) {
  192. return style.rounded || style.diagonals || style.shape != 0;
  193. }
  194. /*
  195. * every shape has these functions:
  196. *
  197. * void SHAPE_init(node_t *n)
  198. * initialize the shape (usually at least its size).
  199. * void SHAPE_free(node_t *n)
  200. * free all memory used by the shape
  201. * port SHAPE_port(node_t *n, char *portname)
  202. * return the aiming point and slope (if constrained)
  203. * of a port.
  204. * int SHAPE_inside(inside_t *inside_context, pointf p, edge_t *e);
  205. * test if point is inside the node shape which is
  206. * assumed convex.
  207. * the point is relative to the node center. the edge
  208. * is passed in case the port affects spline clipping.
  209. * int SHAPE_path(node *n, edge_t *e, int pt, boxf path[], int *nbox)
  210. * create a path for the port of e that touches n,
  211. * return side
  212. * void SHAPE_gencode(GVJ_t *job, node_t *n)
  213. * generate graphics code for a node.
  214. *
  215. * some shapes, polygons in particular, use additional shape control data *
  216. *
  217. */
  218. static shape_functions poly_fns = {
  219. poly_init,
  220. poly_free,
  221. poly_port,
  222. poly_inside,
  223. poly_path,
  224. poly_gencode
  225. };
  226. static shape_functions point_fns = {
  227. point_init,
  228. poly_free,
  229. poly_port,
  230. point_inside,
  231. NULL,
  232. point_gencode
  233. };
  234. static shape_functions record_fns = {
  235. record_init,
  236. record_free,
  237. record_port,
  238. record_inside,
  239. record_path,
  240. record_gencode
  241. };
  242. static shape_functions epsf_fns = {
  243. epsf_init,
  244. epsf_free,
  245. poly_port,
  246. epsf_inside,
  247. NULL,
  248. epsf_gencode
  249. };
  250. static shape_functions star_fns = {
  251. poly_init,
  252. poly_free,
  253. poly_port,
  254. star_inside,
  255. poly_path,
  256. poly_gencode
  257. };
  258. static shape_functions cylinder_fns = {
  259. poly_init,
  260. poly_free,
  261. poly_port,
  262. poly_inside,
  263. poly_path,
  264. poly_gencode
  265. };
  266. static shape_desc Shapes[] = { /* first entry is default for no such shape */
  267. {.name = "box", .fns = &poly_fns, .polygon = &p_box},
  268. {.name = "polygon", .fns = &poly_fns, .polygon = &p_polygon},
  269. {.name = "ellipse", .fns = &poly_fns, .polygon = &p_ellipse},
  270. {.name = "oval", .fns = &poly_fns, .polygon = &p_ellipse},
  271. {.name = "circle", .fns = &poly_fns, .polygon = &p_circle},
  272. {.name = "point", .fns = &point_fns, .polygon = &p_circle},
  273. {.name = "egg", .fns = &poly_fns, .polygon = &p_egg},
  274. {.name = "triangle", .fns = &poly_fns, .polygon = &p_triangle},
  275. {.name = "none", .fns = &poly_fns, .polygon = &p_plaintext},
  276. {.name = "plaintext", .fns = &poly_fns, .polygon = &p_plaintext},
  277. {.name = "plain", .fns = &poly_fns, .polygon = &p_plain},
  278. {.name = "diamond", .fns = &poly_fns, .polygon = &p_diamond},
  279. {.name = "trapezium", .fns = &poly_fns, .polygon = &p_trapezium},
  280. {.name = "parallelogram", .fns = &poly_fns, .polygon = &p_parallelogram},
  281. {.name = "house", .fns = &poly_fns, .polygon = &p_house},
  282. {.name = "pentagon", .fns = &poly_fns, .polygon = &p_pentagon},
  283. {.name = "hexagon", .fns = &poly_fns, .polygon = &p_hexagon},
  284. {.name = "septagon", .fns = &poly_fns, .polygon = &p_septagon},
  285. {.name = "octagon", .fns = &poly_fns, .polygon = &p_octagon},
  286. {.name = "note", .fns = &poly_fns, .polygon = &p_note},
  287. {.name = "tab", .fns = &poly_fns, .polygon = &p_tab},
  288. {.name = "folder", .fns = &poly_fns, .polygon = &p_folder},
  289. {.name = "box3d", .fns = &poly_fns, .polygon = &p_box3d},
  290. {.name = "component", .fns = &poly_fns, .polygon = &p_component},
  291. {.name = "cylinder", .fns = &cylinder_fns, .polygon = &p_cylinder},
  292. {.name = "rect", .fns = &poly_fns, .polygon = &p_box},
  293. {.name = "rectangle", .fns = &poly_fns, .polygon = &p_box},
  294. {.name = "square", .fns = &poly_fns, .polygon = &p_square},
  295. {.name = "doublecircle", .fns = &poly_fns, .polygon = &p_doublecircle},
  296. {.name = "doubleoctagon", .fns = &poly_fns, .polygon = &p_doubleoctagon},
  297. {.name = "tripleoctagon", .fns = &poly_fns, .polygon = &p_tripleoctagon},
  298. {.name = "invtriangle", .fns = &poly_fns, .polygon = &p_invtriangle},
  299. {.name = "invtrapezium", .fns = &poly_fns, .polygon = &p_invtrapezium},
  300. {.name = "invhouse", .fns = &poly_fns, .polygon = &p_invhouse},
  301. {.name = "underline", .fns = &poly_fns, .polygon = &p_underline},
  302. {.name = "Mdiamond", .fns = &poly_fns, .polygon = &p_Mdiamond},
  303. {.name = "Msquare", .fns = &poly_fns, .polygon = &p_Msquare},
  304. {.name = "Mcircle", .fns = &poly_fns, .polygon = &p_Mcircle},
  305. /* biological circuit shapes, as specified by SBOLv*/
  306. /** gene expression symbols **/
  307. {.name = "promoter", .fns = &poly_fns, .polygon = &p_promoter},
  308. {.name = "cds", .fns = &poly_fns, .polygon = &p_cds},
  309. {.name = "terminator", .fns = &poly_fns, .polygon = &p_terminator},
  310. {.name = "utr", .fns = &poly_fns, .polygon = &p_utr},
  311. {.name = "insulator", .fns = &poly_fns, .polygon = &p_insulator},
  312. {.name = "ribosite", .fns = &poly_fns, .polygon = &p_ribosite},
  313. {.name = "rnastab", .fns = &poly_fns, .polygon = &p_rnastab},
  314. {.name = "proteasesite", .fns = &poly_fns, .polygon = &p_proteasesite},
  315. {.name = "proteinstab", .fns = &poly_fns, .polygon = &p_proteinstab},
  316. /** dna construction symbols **/
  317. {.name = "primersite", .fns = &poly_fns, .polygon = &p_primersite},
  318. {.name = "restrictionsite", .fns = &poly_fns, .polygon = &p_restrictionsite},
  319. {.name = "fivepoverhang", .fns = &poly_fns, .polygon = &p_fivepoverhang},
  320. {.name = "threepoverhang", .fns = &poly_fns, .polygon = &p_threepoverhang},
  321. {.name = "noverhang", .fns = &poly_fns, .polygon = &p_noverhang},
  322. {.name = "assembly", .fns = &poly_fns, .polygon = &p_assembly},
  323. {.name = "signature", .fns = &poly_fns, .polygon = &p_signature},
  324. {.name = "rpromoter", .fns = &poly_fns, .polygon = &p_rpromoter},
  325. {.name = "larrow", .fns = &poly_fns, .polygon = &p_larrow},
  326. {.name = "rarrow", .fns = &poly_fns, .polygon = &p_rarrow},
  327. {.name = "lpromoter", .fns = &poly_fns, .polygon = &p_lpromoter},
  328. /* *** shapes other than polygons *** */
  329. {.name = "record", .fns = &record_fns, .polygon = NULL},
  330. {.name = "Mrecord", .fns = &record_fns, .polygon = NULL},
  331. {.name = "epsf", .fns = &epsf_fns, .polygon = NULL},
  332. {.name = "star", .fns = &star_fns, .polygon = &p_star},
  333. {0}
  334. };
  335. static void unrecognized(node_t * n, char *p)
  336. {
  337. agwarningf("node %s, port %s unrecognized\n", agnameof(n), p);
  338. }
  339. static double quant(double val, double q)
  340. {
  341. return ceil(val / q) * q;
  342. }
  343. /* test if both p0 and p1 are on the same side of the line L0,L1 */
  344. static int same_side(pointf p0, pointf p1, pointf L0, pointf L1)
  345. {
  346. int s0, s1;
  347. double a, b, c;
  348. /* a x + b y = c */
  349. a = -(L1.y - L0.y);
  350. b = L1.x - L0.x;
  351. c = a * L0.x + b * L0.y;
  352. s0 = a * p0.x + b * p0.y - c >= 0;
  353. s1 = a * p1.x + b * p1.y - c >= 0;
  354. return s0 == s1;
  355. }
  356. static
  357. char* penColor(GVJ_t * job, node_t * n)
  358. {
  359. char *color;
  360. color = late_nnstring(n, N_color, "");
  361. if (!color[0])
  362. color = DEFAULT_COLOR;
  363. gvrender_set_pencolor(job, color);
  364. return color;
  365. }
  366. static
  367. char *findFillDflt(node_t * n, char *dflt)
  368. {
  369. char *color;
  370. color = late_nnstring(n, N_fillcolor, "");
  371. if (!color[0]) {
  372. /* for backward compatibility, default fill is same as pen */
  373. color = late_nnstring(n, N_color, "");
  374. if (!color[0]) {
  375. color = dflt;
  376. }
  377. }
  378. return color;
  379. }
  380. static
  381. char *findFill(node_t * n)
  382. {
  383. return findFillDflt(n, DEFAULT_FILL);
  384. }
  385. static bool isBox(node_t *n) {
  386. polygon_t *p;
  387. if ((p = ND_shape(n)->polygon)) {
  388. return p->sides == 4 && fabs(fmod(p->orientation, 90)) < 0.5 &&
  389. is_exactly_zero(p->distortion) && is_exactly_zero(p->skew);
  390. }
  391. return false;
  392. }
  393. static bool isEllipse(node_t *n) {
  394. polygon_t *p;
  395. if ((p = ND_shape(n)->polygon)) {
  396. return p->sides <= 2;
  397. }
  398. return false;
  399. }
  400. /// bitwise-OR styles
  401. static graphviz_polygon_style_t style_or(graphviz_polygon_style_t a,
  402. graphviz_polygon_style_t b) {
  403. // bitwise-or-ing the shape does not make sense, so there better only be one
  404. assert(a.shape == 0 || b.shape == 0);
  405. return (graphviz_polygon_style_t){
  406. .filled = a.filled || b.filled,
  407. .radial = a.radial || b.radial,
  408. .rounded = a.rounded || b.rounded,
  409. .diagonals = a.diagonals || b.diagonals,
  410. .auxlabels = a.auxlabels || b.auxlabels,
  411. .invisible = a.invisible || b.invisible,
  412. .striped = a.striped || b.striped,
  413. .dotted = a.dotted || b.dotted,
  414. .dashed = a.dashed || b.dashed,
  415. .wedged = a.wedged || b.wedged,
  416. .underline = a.underline || b.underline,
  417. .fixedshape = a.fixedshape || b.fixedshape,
  418. .shape = a.shape | b.shape,
  419. };
  420. }
  421. static char **checkStyle(node_t *n, graphviz_polygon_style_t *flagp) {
  422. char *style;
  423. char **pstyle = 0;
  424. graphviz_polygon_style_t istyle = {0};
  425. polygon_t *poly;
  426. style = late_nnstring(n, N_style, "");
  427. if (style[0]) {
  428. char **pp;
  429. char **qp;
  430. char *p;
  431. pp = pstyle = parse_style(style);
  432. while ((p = *pp)) {
  433. if (streq(p, "filled")) {
  434. istyle.filled = true;
  435. pp++;
  436. } else if (streq(p, "rounded")) {
  437. istyle.rounded = true;
  438. qp = pp; /* remove rounded from list passed to renderer */
  439. do {
  440. qp++;
  441. *(qp - 1) = *qp;
  442. } while (*qp);
  443. } else if (streq(p, "diagonals")) {
  444. istyle.diagonals = true;
  445. qp = pp; /* remove diagonals from list passed to renderer */
  446. do {
  447. qp++;
  448. *(qp - 1) = *qp;
  449. } while (*qp);
  450. } else if (streq(p, "invis")) {
  451. istyle.invisible = true;
  452. pp++;
  453. } else if (streq(p, "radial")) {
  454. istyle.radial = true;
  455. istyle.filled = true;
  456. qp = pp; /* remove radial from list passed to renderer */
  457. do {
  458. qp++;
  459. *(qp - 1) = *qp;
  460. } while (*qp);
  461. } else if (streq(p, "striped") && isBox(n)) {
  462. istyle.striped = true;
  463. qp = pp; /* remove striped from list passed to renderer */
  464. do {
  465. qp++;
  466. *(qp - 1) = *qp;
  467. } while (*qp);
  468. } else if (streq(p, "wedged") && isEllipse(n)) {
  469. istyle.wedged = true;
  470. qp = pp; /* remove wedged from list passed to renderer */
  471. do {
  472. qp++;
  473. *(qp - 1) = *qp;
  474. } while (*qp);
  475. } else
  476. pp++;
  477. }
  478. }
  479. if ((poly = ND_shape(n)->polygon))
  480. istyle = style_or(istyle, poly->option);
  481. *flagp = istyle;
  482. return pstyle;
  483. }
  484. static graphviz_polygon_style_t stylenode(GVJ_t *job, node_t *n) {
  485. char **pstyle, *s;
  486. graphviz_polygon_style_t istyle = {0};
  487. double penwidth;
  488. if ((pstyle = checkStyle(n, &istyle)))
  489. gvrender_set_style(job, pstyle);
  490. if (N_penwidth && (s = agxget(n, N_penwidth)) && s[0]) {
  491. penwidth = late_double(n, N_penwidth, 1.0, 0.0);
  492. gvrender_set_penwidth(job, penwidth);
  493. }
  494. return istyle;
  495. }
  496. static void Mcircle_hack(GVJ_t * job, node_t * n)
  497. {
  498. double x, y;
  499. pointf AF[2], p;
  500. y = .7500;
  501. x = .6614; /* x^2 + y^2 = 1.0 */
  502. p.y = y * ND_ht(n) / 2.0;
  503. p.x = ND_rw(n) * x; /* assume node is symmetric */
  504. AF[0] = add_pointf(p, ND_coord(n));
  505. AF[1].y = AF[0].y;
  506. AF[1].x = AF[0].x - 2 * p.x;
  507. gvrender_polyline(job, AF, 2);
  508. AF[0].y -= 2 * p.y;
  509. AF[1].y = AF[0].y;
  510. gvrender_polyline(job, AF, 2);
  511. }
  512. static pointf * alloc_interpolation_points(pointf *AF, size_t sides,
  513. graphviz_polygon_style_t style, bool rounded)
  514. {
  515. pointf *B = gv_calloc(4 * sides + 4, sizeof(pointf));
  516. size_t i = 0;
  517. pointf p0, p1;
  518. double dx, dy, t;
  519. /* rbconst is distance offset from a corner of the polygon.
  520. * It should be the same for every corner, and also never
  521. * bigger than one-third the length of a side.
  522. */
  523. double rbconst = RBCONST;
  524. for (size_t seg = 0; seg < sides; seg++) {
  525. p0 = AF[seg];
  526. if (seg + 1 < sides)
  527. p1 = AF[seg + 1];
  528. else
  529. p1 = AF[0];
  530. dx = p1.x - p0.x;
  531. dy = p1.y - p0.y;
  532. const double d = hypot(dx, dy);
  533. rbconst = fmin(rbconst, d / 3.0);
  534. }
  535. for (size_t seg = 0; seg < sides; seg++) {
  536. p0 = AF[seg];
  537. if (seg + 1 < sides)
  538. p1 = AF[seg + 1];
  539. else
  540. p1 = AF[0];
  541. dx = p1.x - p0.x;
  542. dy = p1.y - p0.y;
  543. const double d = hypot(dx, dy);
  544. t = rbconst / d;
  545. if (style.shape == BOX3D || style.shape == COMPONENT)
  546. t /= 3;
  547. else if (style.shape == DOGEAR)
  548. t /= 2;
  549. if (!rounded)
  550. B[i++] = p0;
  551. else
  552. B[i++] = interpolate_pointf(RBCURVE * t, p0, p1);
  553. B[i++] = interpolate_pointf(t, p0, p1);
  554. B[i++] = interpolate_pointf(1.0 - t, p0, p1);
  555. if (rounded)
  556. B[i++] = interpolate_pointf(1.0 - RBCURVE * t, p0, p1);
  557. }
  558. B[i++] = B[0];
  559. B[i++] = B[1];
  560. B[i++] = B[2];
  561. return B;
  562. }
  563. /**
  564. * @brief draws polygons with diagonals on corners
  565. *
  566. * Diagonals are weird. Rewrite someday.
  567. */
  568. static void diagonals_draw(GVJ_t *job, pointf *AF, size_t sides,
  569. graphviz_polygon_style_t style, int filled)
  570. {
  571. pointf *B = alloc_interpolation_points(AF, sides, style, false);
  572. gvrender_polygon(job, AF, sides, filled);
  573. for (size_t seg = 0; seg < sides; seg++) {
  574. pointf C[] = {B[3 * seg + 2], B[3 * seg + 4]};
  575. gvrender_polyline(job, C, 2);
  576. }
  577. free(B);
  578. }
  579. /**
  580. * @brief draws rounded polygons with
  581. * [Bézier curve](https://en.wikipedia.org/wiki/Bézier_curve)
  582. *
  583. * For example, a rounded star looks like a cartoon starfish.
  584. */
  585. static void rounded_draw(GVJ_t *job, pointf *AF, size_t sides,
  586. graphviz_polygon_style_t style, int filled)
  587. {
  588. size_t i = 0;
  589. pointf *B = alloc_interpolation_points(AF, sides, style, true);
  590. pointf *pts = gv_calloc(6 * sides + 2, sizeof(pointf));
  591. for (size_t seg = 0; seg < sides; seg++) {
  592. pts[i++] = B[4 * seg];
  593. pts[i++] = B[4 * seg + 1];
  594. pts[i++] = B[4 * seg + 1];
  595. pts[i++] = B[4 * seg + 2];
  596. pts[i++] = B[4 * seg + 2];
  597. pts[i++] = B[4 * seg + 3];
  598. }
  599. pts[i++] = pts[0];
  600. pts[i++] = pts[1];
  601. gvrender_beziercurve(job, pts + 1, i - 1, filled);
  602. free(pts);
  603. free(B);
  604. }
  605. /**
  606. * @file
  607. * ~~~~
  608. * y
  609. * 🡑
  610. * │
  611. * │
  612. * │ line[1]
  613. * │ ⟋
  614. * mid_y ┤ middle
  615. * │ ⟋
  616. * │line[0] x
  617. * ─┼─────┬───────────🡒
  618. * mid_x
  619. * ~~~~
  620. */
  621. /**
  622. * @brief X coordinate of line midpoint
  623. * @param line two points
  624. * @returns X coordinate of midpoint
  625. */
  626. static double mid_x(const pointf line[2]) {
  627. return (line[0].x + line[1].x) / 2;
  628. }
  629. /**
  630. * @brief Y coordinate of line midpoint
  631. * @param line two points
  632. * @returns Y coordinate of midpoint
  633. */
  634. static double mid_y(const pointf line[2]) {
  635. return (line[0].y + line[1].y) / 2;
  636. }
  637. /**
  638. * @brief Handle some special graphical cases, such as rounding the shape,
  639. * adding diagonals at corners, or drawing certain non-simple figures.
  640. *
  641. * Any drawing done here should assume fillcolors, pencolors, etc.
  642. * have been set by the calling routine. Normally, the drawing should
  643. * consist of a region, filled or unfilled, followed by additional line
  644. * segments. A single fill is necessary for gradient colors to work.
  645. */
  646. void round_corners(GVJ_t *job, pointf *AF, size_t sides,
  647. graphviz_polygon_style_t style, int filled) {
  648. assert(job != NULL);
  649. assert(AF != NULL);
  650. assert(sides > 0);
  651. assert(memcmp(&style, &(graphviz_polygon_style_t){0}, sizeof(style)) != 0);
  652. pointf *B, C[5], *D;
  653. struct {
  654. unsigned shape: 7;
  655. } mode = {0};
  656. if (style.diagonals)
  657. return diagonals_draw(job, AF, sides, style, filled);
  658. else if (style.shape != 0)
  659. mode.shape = style.shape;
  660. else if (style.rounded)
  661. return rounded_draw(job, AF, sides, style, filled);
  662. else
  663. UNREACHABLE();
  664. if (mode.shape == CYLINDER) {
  665. cylinder_draw(job, AF, sides, filled);
  666. return;
  667. }
  668. B = alloc_interpolation_points(AF, sides, style, false);
  669. switch (mode.shape) {
  670. case DOGEAR:
  671. /* Add the cutoff edge. */
  672. D = gv_calloc(sides + 1, sizeof(pointf));
  673. for (size_t seg = 1; seg < sides; seg++)
  674. D[seg] = AF[seg];
  675. D[0] = B[3 * (sides - 1) + 4];
  676. D[sides] = B[3 * (sides - 1) + 2];
  677. gvrender_polygon(job, D, sides + 1, filled);
  678. free(D);
  679. /* Draw the inner edge. */
  680. const size_t sseg = sides - 1;
  681. C[0] = B[3 * sseg + 2];
  682. C[1] = B[3 * sseg + 4];
  683. C[2].x = C[1].x + (C[0].x - B[3 * sseg + 3].x);
  684. C[2].y = C[1].y + (C[0].y - B[3 * sseg + 3].y);
  685. gvrender_polyline(job, C + 1, 2);
  686. C[1] = C[2];
  687. gvrender_polyline(job, C, 2);
  688. break;
  689. case TAB:
  690. /*
  691. * Adjust the perimeter for the protrusions.
  692. *
  693. * D[3] ×──× D[2]
  694. * │ │ B[1]
  695. * B[3] ×──×──────────×──× AF[0]=B[0]=D[0]
  696. * │ B[2]=D[1] │
  697. * B[4] × │
  698. * │ │
  699. * B[5] × │
  700. * └────────────────┘
  701. *
  702. */
  703. /* Add the tab edges. */
  704. D = gv_calloc(sides + 2, sizeof(pointf));
  705. D[0] = AF[0];
  706. D[1] = B[2];
  707. D[2].x = B[2].x + (B[3].x - B[4].x) / 3;
  708. D[2].y = B[2].y + (B[3].y - B[4].y) / 3;
  709. D[3].x = B[3].x + (B[3].x - B[4].x) / 3;
  710. D[3].y = B[3].y + (B[3].y - B[4].y) / 3;
  711. for (size_t seg = 4; seg < sides + 2; seg++)
  712. D[seg] = AF[seg - 2];
  713. gvrender_polygon(job, D, sides + 2, filled);
  714. free(D);
  715. /* Draw the inner edge. */
  716. C[0] = B[3];
  717. C[1] = B[2];
  718. gvrender_polyline(job, C, 2);
  719. break;
  720. case FOLDER:
  721. /*
  722. * Adjust the perimeter for the protrusions.
  723. *
  724. * D[2] ×────× D[1]
  725. * B[3]= ╱ ╲
  726. * D[4] ×──×────× × × AF[0]=B[0]=D[0]
  727. * │ B[2] D[3] B[1]│
  728. * B[4] × │
  729. * │ │
  730. * B[5] × │
  731. * └────────────────┘
  732. *
  733. */
  734. /* Add the folder edges. */
  735. D = gv_calloc(sides + 3, sizeof(pointf));
  736. D[0] = AF[0];
  737. D[1].x = AF[0].x - (AF[0].x - B[1].x) / 4;
  738. D[1].y = AF[0].y + (B[3].y - B[4].y) / 3;
  739. D[2].x = AF[0].x - 2 * (AF[0].x - B[1].x);
  740. D[2].y = D[1].y;
  741. D[3].x = AF[0].x - 2.25 * (AF[0].x - B[1].x);
  742. D[3].y = B[3].y;
  743. D[4].x = B[3].x;
  744. D[4].y = B[3].y;
  745. for (size_t seg = 4; seg < sides + 3; seg++)
  746. D[seg] = AF[seg - 3];
  747. gvrender_polygon(job, D, sides + 3, filled);
  748. free(D);
  749. break;
  750. case BOX3D:
  751. assert(sides == 4);
  752. /* Adjust for the cutoff edges. */
  753. D = gv_calloc(sides + 2, sizeof(pointf));
  754. D[0] = AF[0];
  755. D[1] = B[2];
  756. D[2] = B[4];
  757. D[3] = AF[2];
  758. D[4] = B[8];
  759. D[5] = B[10];
  760. gvrender_polygon(job, D, sides + 2, filled);
  761. free(D);
  762. /* Draw the inner vertices. */
  763. C[0].x = B[1].x + (B[11].x - B[0].x);
  764. C[0].y = B[1].y + (B[11].y - B[0].y);
  765. C[1] = B[4];
  766. gvrender_polyline(job, C, 2);
  767. C[1] = B[8];
  768. gvrender_polyline(job, C, 2);
  769. C[1] = B[0];
  770. gvrender_polyline(job, C, 2);
  771. break;
  772. case COMPONENT:
  773. assert(sides == 4);
  774. /*
  775. * Adjust the perimeter for the protrusions.
  776. *
  777. * D[1] ×────────────────× D[0]
  778. * │ │
  779. * 3×───×2──┐ │
  780. * │ │ │
  781. * 4×───×5──┘ │
  782. * │ │
  783. * 7×───×6──┐ │
  784. * │ │ │
  785. * 8×───×9──┘ │
  786. * │ │
  787. * 10×────────────────× D[11]
  788. *
  789. */
  790. D = gv_calloc(sides + 8, sizeof(pointf));
  791. D[0] = AF[0];
  792. D[1] = AF[1];
  793. D[2].x = B[3].x + (B[4].x - B[3].x);
  794. D[2].y = B[3].y + (B[4].y - B[3].y);
  795. D[3].x = D[2].x + (B[3].x - B[2].x);
  796. D[3].y = D[2].y + (B[3].y - B[2].y);
  797. D[4].x = D[3].x + (B[4].x - B[3].x);
  798. D[4].y = D[3].y + (B[4].y - B[3].y);
  799. D[5].x = D[4].x + (D[2].x - D[3].x);
  800. D[5].y = D[4].y + (D[2].y - D[3].y);
  801. D[9].x = B[6].x + (B[5].x - B[6].x);
  802. D[9].y = B[6].y + (B[5].y - B[6].y);
  803. D[8].x = D[9].x + (B[6].x - B[7].x);
  804. D[8].y = D[9].y + (B[6].y - B[7].y);
  805. D[7].x = D[8].x + (B[5].x - B[6].x);
  806. D[7].y = D[8].y + (B[5].y - B[6].y);
  807. D[6].x = D[7].x + (D[9].x - D[8].x);
  808. D[6].y = D[7].y + (D[9].y - D[8].y);
  809. D[10] = AF[2];
  810. D[11] = AF[3];
  811. gvrender_polygon(job, D, sides + 8, filled);
  812. /* Draw the internal vertices. */
  813. C[0] = D[2];
  814. C[1].x = D[2].x - (D[3].x - D[2].x);
  815. C[1].y = D[2].y - (D[3].y - D[2].y);
  816. C[2].x = C[1].x + (D[4].x - D[3].x);
  817. C[2].y = C[1].y + (D[4].y - D[3].y);
  818. C[3] = D[5];
  819. gvrender_polyline(job, C, 4);
  820. C[0] = D[6];
  821. C[1].x = D[6].x - (D[7].x - D[6].x);
  822. C[1].y = D[6].y - (D[7].y - D[6].y);
  823. C[2].x = C[1].x + (D[8].x - D[7].x);
  824. C[2].y = C[1].y + (D[8].y - D[7].y);
  825. C[3] = D[9];
  826. gvrender_polyline(job, C, 4);
  827. free(D);
  828. break;
  829. case PROMOTER:
  830. /*
  831. * L-shaped arrow on a center line, scales in the x direction
  832. *
  833. *
  834. * D[1] │╲
  835. * ×────────────────× ╲
  836. * │ D[0] ╲
  837. * │ ╲
  838. * │ ╱
  839. * │ D[5] ╱
  840. * │ ┌───────× ╱
  841. * │ │ │╱
  842. * ─────┴────────┴─────────────
  843. */
  844. /* Add the tab edges. */
  845. //the arrow's thickness is (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
  846. // the thickness is substituted with (AF[0].x - AF[1].x)/8 to make it scalable
  847. // in the y with label length
  848. D = gv_calloc(sides + 5, sizeof(pointf));
  849. D[0].x = mid_x(AF) + (AF[0].x - AF[1].x)/8; //x_center + width
  850. D[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)*3/2; //D[4].y + width
  851. D[1].x = mid_x(AF) - (AF[0].x - AF[1].x)/4; //x_center - 2*width
  852. D[1].y = D[0].y;
  853. D[2].x = D[1].x;
  854. D[2].y = mid_y(&AF[1]);
  855. D[3].x = D[2].x + (B[2].x - B[3].x)/2; //D[2].x + width
  856. D[3].y = mid_y(&AF[1]);
  857. D[4].x = D[3].x;
  858. D[4].y = mid_y(&AF[1]) + (B[3].y-B[4].y); //highest cds point
  859. D[5].x = D[0].x;
  860. D[5].y = D[4].y; //highest cds point
  861. D[6].x = D[0].x;
  862. D[6].y = D[4].y - (B[3].y-B[4].y)/4; //D[4].y - width/2
  863. D[7].x = D[6].x + (B[2].x - B[3].x); //D[6].x + 2*width
  864. D[7].y = D[6].y + (B[3].y - B[4].y)/2; //D[6].y + width
  865. D[8].x = D[0].x;
  866. D[8].y = D[0].y + (B[3].y - B[4].y)/4;//D[0].y + width/2
  867. gvrender_polygon(job, D, sides + 5, filled);
  868. /*dsDNA line*/
  869. C[0].x = AF[1].x;
  870. C[0].y = mid_y(&AF[1]);
  871. C[1].x = AF[0].x;
  872. C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
  873. gvrender_polyline(job, C, 2);
  874. free(D);
  875. break;
  876. case CDS:
  877. /*
  878. * arrow without the protrusions, scales normally
  879. *
  880. *
  881. * D[1] = AF[1]
  882. * ×────────────────×╲
  883. * │ D[0]╲
  884. * │ ╲
  885. * │ ╱
  886. * │ ╱
  887. * ×────────────────×╱
  888. * D[3]
  889. *
  890. */
  891. D = gv_calloc(sides + 1, sizeof(pointf));
  892. D[0].x = B[1].x;
  893. D[0].y = B[1].y - (B[3].y - B[4].y)/2;
  894. D[1].x = B[3].x;
  895. D[1].y = B[3].y - (B[3].y - B[4].y)/2;
  896. D[2].x = AF[2].x;
  897. D[2].y = AF[2].y + (B[3].y - B[4].y)/2;
  898. D[3].x = B[1].x;
  899. D[3].y = AF[2].y + (B[3].y - B[4].y)/2;
  900. D[4].y = AF[0].y - (AF[0].y - AF[3].y)/2;
  901. D[4].x = AF[0].x;
  902. gvrender_polygon(job, D, sides + 1, filled);
  903. free(D);
  904. break;
  905. case TERMINATOR:
  906. /*
  907. * T-shape, does not scale, always in the center
  908. *
  909. *
  910. * D[4]
  911. * ×────────────────×
  912. * │ D[3]
  913. * │ │
  914. * │ │
  915. * │ D[6] D[1] │
  916. * D[5]×───× ×────× D[2]
  917. * │ │
  918. * ─────────┴───────×─D[0]──────
  919. */
  920. //width units are (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
  921. D = gv_calloc(sides + 4, sizeof(pointf));
  922. D[0].x = mid_x(AF) + (B[2].x-B[3].x)/4; //x_center + width/2
  923. D[0].y = mid_y(&AF[1]);
  924. D[1].x = D[0].x;
  925. D[1].y = D[0].y + (B[3].y-B[4].y)/2;
  926. D[2].x = D[1].x + (B[2].x-B[3].x)/2;
  927. D[2].y = D[1].y;
  928. D[3].x = D[2].x;
  929. D[3].y = D[2].y + (B[3].y-B[4].y)/2;
  930. D[4].x = mid_x(AF) - (B[2].x-B[3].x)*3/4; //D[3].y mirrored across the center
  931. D[4].y = D[3].y;
  932. D[5].x = D[4].x;
  933. D[5].y = D[2].y;
  934. D[6].x = mid_x(AF) - (B[2].x-B[3].x)/4; //D[1].x mirrored across the center
  935. D[6].y = D[1].y;
  936. D[7].x = D[6].x;
  937. D[7].y = D[0].y;
  938. gvrender_polygon(job, D, sides + 4, filled);
  939. /*dsDNA line*/
  940. C[0].x = AF[1].x;
  941. C[0].y = mid_y(&AF[1]);
  942. C[1].x = AF[0].x;
  943. C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
  944. gvrender_polyline(job, C, 2);
  945. free(D);
  946. break;
  947. case UTR:
  948. /*
  949. * half-octagon with line, does not scale, always in center
  950. *
  951. * D[3]
  952. * ───── D[2]
  953. * ╱ ╲
  954. * ╱ ╲ D[1]
  955. * │ │
  956. * ─────┴───────┴───────
  957. * D[0]
  958. *
  959. *
  960. *
  961. */
  962. //width units are (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
  963. D = gv_calloc(sides + 2, sizeof(pointf));
  964. D[0].x = mid_x(AF) + (B[2].x-B[3].x)*3/4; //x_center+width
  965. D[0].y = mid_y(&AF[1]);
  966. D[1].x = D[0].x;
  967. D[1].y = D[0].y + (B[3].y-B[4].y)/4; //D[0].y+width/2
  968. D[2].x = mid_x(AF) + (B[2].x-B[3].x)/4; //x_center+width/2
  969. D[2].y = D[1].y + (B[3].y-B[4].y)/2; //D[1].y+width
  970. D[3].x = mid_x(AF) - (B[2].x-B[3].x)/4; //D[2].x mirrored across the center
  971. D[3].y = D[2].y;
  972. D[4].x = mid_x(AF) - (B[2].x-B[3].x)*3/4;
  973. D[4].y = D[1].y;
  974. D[5].x = D[4].x;
  975. D[5].y = D[0].y;
  976. gvrender_polygon(job, D, sides + 2, filled);
  977. /*dsDNA line*/
  978. C[0].x = AF[1].x;
  979. C[0].y = mid_y(&AF[1]);
  980. C[1].x = AF[0].x;
  981. C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
  982. gvrender_polyline(job, C, 2);
  983. free(D);
  984. break;
  985. case PRIMERSITE:
  986. /*
  987. * half arrow shape, scales in the x-direction
  988. * D[1]
  989. * │╲
  990. * │ ╲
  991. * │ ╲
  992. * ┌───────────┘ ╲
  993. * │ ╲
  994. * └─────────────────╲ D[0]
  995. *
  996. * ────────────────────────────────
  997. *
  998. */
  999. //width units are (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
  1000. // the thickness is substituted with (AF[0].x - AF[1].x)/8 to make it scalable
  1001. // in the y with label length
  1002. D = gv_calloc(sides + 1, sizeof(pointf));
  1003. D[0].x = mid_x(AF) + (B[2].x-B[3].x);//x_center + width*2
  1004. D[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)/4;//y_center + 1/2 width
  1005. D[1].x = D[0].x - (B[2].x-B[3].x); //x_center
  1006. D[1].y = D[0].y + (B[3].y-B[4].y);
  1007. D[2].x = D[1].x;
  1008. D[2].y = D[0].y + (B[3].y-B[4].y)/2;
  1009. D[3].x = mid_x(AF) - (AF[0].x - AF[1].x)/4;//x_center - 2*(scalable width)
  1010. D[3].y = D[2].y;
  1011. D[4].x = D[3].x;
  1012. D[4].y = D[0].y;
  1013. gvrender_polygon(job, D, sides + 1, filled);
  1014. /*dsDNA line*/
  1015. C[0].x = AF[1].x;
  1016. C[0].y = mid_y(&AF[1]);
  1017. C[1].x = AF[0].x;
  1018. C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
  1019. gvrender_polyline(job, C, 2);
  1020. free(D);
  1021. break;
  1022. case RESTRICTIONSITE:
  1023. /*
  1024. * zigzag shape, scales in the x-direction (only the middle section)
  1025. *
  1026. *
  1027. * ┌───D[2]
  1028. * │ └──────── D[0]
  1029. * ────┤ ├────
  1030. * └────────┐ │
  1031. * D[4] └─── D[7]
  1032. *
  1033. *
  1034. *
  1035. */
  1036. //width units are (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
  1037. // the thickness is substituted with (AF[0].x - AF[1].x)/8 to make it scalable
  1038. // in the y with label length
  1039. D = gv_calloc(sides + 4, sizeof(pointf));
  1040. D[0].x = mid_x(AF) + (AF[0].x - AF[1].x)/8 + (B[2].x-B[3].x)/2;//x_center + scalable_width + width
  1041. D[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)/4;//y_center + 1/2 width
  1042. D[1].x = mid_x(AF) - (AF[0].x - AF[1].x)/8; //x_center - width
  1043. D[1].y = D[0].y;
  1044. D[2].x = D[1].x;
  1045. D[2].y = D[1].y + (B[3].y-B[4].y)/2;
  1046. D[3].x = D[2].x - (B[2].x-B[3].x)/2; //D[2].x - width
  1047. D[3].y = D[2].y;
  1048. D[4].x = D[3].x;
  1049. D[4].y = mid_y(&AF[1]) - (B[3].y-B[4].y)/4; //y_center - 1/2(width)
  1050. D[5].x = D[0].x - (B[2].x-B[3].x)/2;
  1051. D[5].y = D[4].y;
  1052. D[6].x = D[5].x;
  1053. D[6].y = D[5].y - (B[3].y-B[4].y)/2;
  1054. D[7].x = D[0].x;
  1055. D[7].y = D[6].y;
  1056. gvrender_polygon(job, D, sides + 4, filled);
  1057. /*dsDNA line left half*/
  1058. C[0].x = AF[1].x;
  1059. C[0].y = mid_y(&AF[1]);
  1060. C[1].x = D[4].x;
  1061. C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
  1062. gvrender_polyline(job, C, 2);
  1063. /*dsDNA line right half*/
  1064. C[0].x = D[7].x;
  1065. C[0].y = mid_y(&AF[1]);
  1066. C[1].x = AF[0].x;
  1067. C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
  1068. gvrender_polyline(job, C, 2);
  1069. free(D);
  1070. break;
  1071. case FIVEPOVERHANG:
  1072. /*
  1073. * does not scale, on the left side
  1074. *
  1075. * D[3]──────D[2]
  1076. * │ │
  1077. * D[0]──────D[1]
  1078. * ┌────┐ ────────────
  1079. * │ │
  1080. * D[0]──D[1]
  1081. *
  1082. *
  1083. *
  1084. */
  1085. //width units are (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
  1086. // the thickness is substituted with (AF[0].x - AF[1].x)/8 to make it scalable
  1087. // in the y with label length
  1088. D = gv_calloc(sides, sizeof(pointf));
  1089. D[0].x = AF[1].x;//the very left edge
  1090. D[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)/8;//y_center + 1/4 width
  1091. D[1].x = D[0].x + 2*(B[2].x-B[3].x);
  1092. D[1].y = D[0].y;
  1093. D[2].x = D[1].x;
  1094. D[2].y = D[1].y + (B[3].y-B[4].y)/2;
  1095. D[3].x = D[0].x;
  1096. D[3].y = D[2].y;
  1097. gvrender_polygon(job, D, sides, filled);
  1098. /*second, lower shape*/
  1099. free(D);
  1100. D = gv_calloc(sides, sizeof(pointf));
  1101. D[0].x = AF[1].x + (B[2].x-B[3].x);
  1102. D[0].y = mid_y(&AF[1]) - (B[3].y-B[4].y)*5/8; //y_center - 5/4 width
  1103. D[1].x = D[0].x + (B[2].x-B[3].x);
  1104. D[1].y = D[0].y;
  1105. D[2].x = D[1].x;
  1106. D[2].y = D[1].y + (B[3].y-B[4].y)/2;
  1107. D[3].x = D[0].x;
  1108. D[3].y = D[2].y;
  1109. gvrender_polygon(job, D, sides, filled);
  1110. /*dsDNA line right half*/
  1111. C[0].x = D[1].x;
  1112. C[0].y = mid_y(&AF[1]);
  1113. C[1].x = AF[0].x;
  1114. C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
  1115. gvrender_polyline(job, C, 2);
  1116. free(D);
  1117. break;
  1118. case THREEPOVERHANG:
  1119. /*
  1120. * does not scale, on the right side
  1121. *
  1122. * D[2]──────D[1]
  1123. * │ │
  1124. * ─────── D[3]──────D[0]
  1125. * ┌────┐ D[1]
  1126. * │ │
  1127. * D[3]──D[0]
  1128. *
  1129. *
  1130. *
  1131. */
  1132. //width units are (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
  1133. // the thickness is substituted with (AF[0].x - AF[1].x)/8 to make it scalable
  1134. // in the y with label length
  1135. D = gv_calloc(sides, sizeof(pointf));
  1136. D[0].x = AF[0].x;//the very right edge
  1137. D[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)/8;//y_center + 1/4 width
  1138. D[1].x = D[0].x;
  1139. D[1].y = D[0].y + (B[3].y-B[4].y)/2;
  1140. D[2].x = D[1].x - 2*(B[3].y-B[4].y);
  1141. D[2].y = D[1].y;
  1142. D[3].x = D[2].x;
  1143. D[3].y = D[0].y;
  1144. gvrender_polygon(job, D, sides, filled);
  1145. /*second, lower shape*/
  1146. free(D);
  1147. D = gv_calloc(sides, sizeof(pointf));
  1148. D[0].x = AF[0].x - (B[2].x-B[3].x);
  1149. D[0].y = mid_y(&AF[1]) - (B[3].y-B[4].y)*5/8; //y_center - 5/4 width
  1150. D[1].x = D[0].x;
  1151. D[1].y = D[0].y + (B[3].y-B[4].y)/2;
  1152. D[2].x = D[1].x - (B[3].y-B[4].y);
  1153. D[2].y = D[1].y;
  1154. D[3].x = D[2].x;
  1155. D[3].y = D[0].y;
  1156. gvrender_polygon(job, D, sides, filled);
  1157. /*dsDNA line left half*/
  1158. C[0].x = AF[1].x;
  1159. C[0].y = mid_y(&AF[1]);
  1160. C[1].x = D[3].x;
  1161. C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
  1162. gvrender_polyline(job, C, 2);
  1163. free(D);
  1164. break;
  1165. case NOVERHANG:
  1166. /*
  1167. * does not scale
  1168. *
  1169. * D[3]──────D[2] D[3]──────D[2]
  1170. * │ │ │ │
  1171. * ───D[0]──────D[1] D[0]──────D[1]────
  1172. * D[3]──────D[2] D[3]──────D[2]
  1173. * │ │ │ │
  1174. * D[0]──────D[1] D[0]──────D[1]
  1175. *
  1176. *
  1177. *
  1178. *
  1179. */
  1180. //width units are (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
  1181. // the thickness is substituted with (AF[0].x - AF[1].x)/8 to make it scalable
  1182. // in the y with label length
  1183. /*upper left rectangle*/
  1184. D = gv_calloc(sides, sizeof(pointf));
  1185. D[0].x = mid_x(AF) - (B[2].x-B[3].x)*9/8; //x_center - 2*width - 1/4*width
  1186. D[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)/8;//y_center + 1/4 width
  1187. D[1].x = D[0].x + (B[2].x-B[3].x);
  1188. D[1].y = D[0].y;
  1189. D[2].x = D[1].x;
  1190. D[2].y = D[1].y + (B[3].y-B[4].y)/2;
  1191. D[3].x = D[0].x;
  1192. D[3].y = D[2].y;
  1193. gvrender_polygon(job, D, sides, filled);
  1194. /*lower, left rectangle*/
  1195. free(D);
  1196. D = gv_calloc(sides, sizeof(pointf));
  1197. D[0].x = mid_x(AF) - (B[2].x-B[3].x)*9/8; //x_center - 2*width - 1/4*width
  1198. D[0].y = mid_y(&AF[1]) - (B[3].y-B[4].y)*5/8;//y_center - width - 1/4 width
  1199. D[1].x = D[0].x + (B[2].x-B[3].x);
  1200. D[1].y = D[0].y;
  1201. D[2].x = D[1].x;
  1202. D[2].y = D[1].y + (B[3].y-B[4].y)/2;
  1203. D[3].x = D[0].x;
  1204. D[3].y = D[2].y;
  1205. gvrender_polygon(job, D, sides, filled);
  1206. /*lower, right rectangle*/
  1207. free(D);
  1208. D = gv_calloc(sides, sizeof(pointf));
  1209. D[0].x = mid_x(AF) + (B[2].x-B[3].x)/8; //x_center + 1/4*width
  1210. D[0].y = mid_y(&AF[1]) - (B[3].y-B[4].y)*5/8;//y_center - width - 1/4 width
  1211. D[1].x = D[0].x + (B[2].x-B[3].x);
  1212. D[1].y = D[0].y;
  1213. D[2].x = D[1].x;
  1214. D[2].y = D[1].y + (B[3].y-B[4].y)/2;
  1215. D[3].x = D[0].x;
  1216. D[3].y = D[2].y;
  1217. gvrender_polygon(job, D, sides, filled);
  1218. /*upper, right rectangle*/
  1219. free(D);
  1220. D = gv_calloc(sides, sizeof(pointf));
  1221. D[0].x = mid_x(AF) + (B[2].x-B[3].x)/8; //x_center + 1/4*width
  1222. D[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)/8;//y_center - width - 1/4 width
  1223. D[1].x = D[0].x + (B[2].x-B[3].x);
  1224. D[1].y = D[0].y;
  1225. D[2].x = D[1].x;
  1226. D[2].y = D[1].y + (B[3].y-B[4].y)/2;
  1227. D[3].x = D[0].x;
  1228. D[3].y = D[2].y;
  1229. gvrender_polygon(job, D, sides, filled);
  1230. /*dsDNA line right half*/
  1231. C[0].x = D[1].x;
  1232. C[0].y = mid_y(&AF[1]);
  1233. C[1].x = AF[0].x;
  1234. C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
  1235. gvrender_polyline(job, C, 2);
  1236. /*dsDNA line left half*/
  1237. C[0].x = mid_x(AF) - (B[2].x-B[3].x)*9/8; //D[0].x of of the left rectangles
  1238. C[0].y = mid_y(&AF[1]);
  1239. C[1].x = AF[1].x;
  1240. C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
  1241. gvrender_polyline(job, C, 2);
  1242. free(D);
  1243. break;
  1244. case ASSEMBLY:
  1245. /*
  1246. * does not scale
  1247. *
  1248. * D[3]──────────D[2]
  1249. * │ │
  1250. * D[0]──────────D[1]
  1251. * ──── ─────────
  1252. * D[3]──────────D[2]
  1253. * │ │
  1254. * D[0]──────────D[1]
  1255. *
  1256. */
  1257. //width units are (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
  1258. // the thickness is substituted with (AF[0].x - AF[1].x)/8 to make it scalable
  1259. // in the y with label length
  1260. D = gv_calloc(sides, sizeof(pointf));
  1261. D[0].x = mid_x(AF) - (B[2].x-B[3].x); //x_center - 2*width
  1262. D[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)/8;//y_center + 1/4 width
  1263. D[1].x = D[0].x + 2*(B[2].x-B[3].x);
  1264. D[1].y = D[0].y;
  1265. D[2].x = D[1].x;
  1266. D[2].y = D[1].y + (B[3].y-B[4].y)/2;
  1267. D[3].x = D[0].x;
  1268. D[3].y = D[2].y;
  1269. gvrender_polygon(job, D, sides, filled);
  1270. /*second, lower shape*/
  1271. free(D);
  1272. D = gv_calloc(sides, sizeof(pointf));
  1273. D[0].x = mid_x(AF) - (B[2].x-B[3].x); //x_center - 2*width
  1274. D[0].y = mid_y(&AF[1]) - (B[3].y-B[4].y)*5/8;//y_center - width - 1/4 width
  1275. D[1].x = D[0].x + 2*(B[2].x-B[3].x);
  1276. D[1].y = D[0].y;
  1277. D[2].x = D[1].x;
  1278. D[2].y = D[1].y + (B[3].y-B[4].y)/2;
  1279. D[3].x = D[0].x;
  1280. D[3].y = D[2].y;
  1281. gvrender_polygon(job, D, sides, filled);
  1282. /*dsDNA line right half*/
  1283. C[0].x = D[1].x;
  1284. C[0].y = mid_y(&AF[1]);
  1285. C[1].x = AF[0].x;
  1286. C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
  1287. gvrender_polyline(job, C, 2);
  1288. /*dsDNA line left half*/
  1289. C[0].x = AF[1].x;
  1290. C[0].y = mid_y(&AF[1]);
  1291. C[1].x = D[0].x;
  1292. C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
  1293. gvrender_polyline(job, C, 2);
  1294. free(D);
  1295. break;
  1296. case SIGNATURE:
  1297. /*
  1298. *
  1299. *
  1300. * ┌──────────────┐
  1301. * │ │
  1302. * │x │
  1303. * │_____________ │
  1304. * └──────────────┘
  1305. */
  1306. //width units are (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
  1307. // the thickness is substituted with (AF[0].x - AF[1].x)/8 to make it scalable
  1308. // in the y with label length
  1309. D = gv_calloc(sides, sizeof(pointf));
  1310. D[0].x = AF[0].x;
  1311. D[0].y = B[1].y - (B[3].y - B[4].y)/2;
  1312. D[1].x = B[3].x;
  1313. D[1].y = B[3].y - (B[3].y - B[4].y)/2;
  1314. D[2].x = AF[2].x;
  1315. D[2].y = AF[2].y + (B[3].y - B[4].y)/2;
  1316. D[3].x = AF[0].x;
  1317. D[3].y = AF[2].y + (B[3].y - B[4].y)/2;
  1318. gvrender_polygon(job, D, sides, filled);
  1319. /* "\" of the X*/
  1320. C[0].x = AF[1].x + (B[2].x-B[3].x)/4;
  1321. C[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)/8; //y_center + 1/4 width
  1322. C[1].x = C[0].x + (B[2].x-B[3].x)/4;//C[0].x + width/2
  1323. C[1].y = C[0].y - (B[3].y-B[4].y)/4;//C[0].y - width/2
  1324. gvrender_polyline(job, C, 2);
  1325. /*"/" of the X*/
  1326. C[0].x = AF[1].x + (B[2].x-B[3].x)/4;
  1327. C[0].y = mid_y(&AF[1]) - (B[3].y-B[4].y)/8; //y_center - 1/4 width
  1328. C[1].x = C[0].x + (B[2].x-B[3].x)/4;//C[0].x + width/2
  1329. C[1].y = C[0].y + (B[3].y-B[4].y)/4;//C[0].y + width/2
  1330. gvrender_polyline(job, C, 2);
  1331. /*bottom line*/
  1332. C[0].x = AF[1].x + (B[2].x-B[3].x)/4;
  1333. C[0].y = AF[2].y + (B[3].y-B[4].y)*3/4;
  1334. C[1].x = AF[0].x - (B[2].x-B[3].x)/4;
  1335. C[1].y = C[0].y;
  1336. gvrender_polyline(job, C, 2);
  1337. free(D);
  1338. break;
  1339. case INSULATOR:
  1340. /*
  1341. * double square
  1342. *
  1343. * ┌─────┐
  1344. *──┤ ┌─┐ ├───
  1345. * │ └─┘ │
  1346. * └─────┘
  1347. *
  1348. */
  1349. //width units are (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
  1350. D = gv_calloc(sides, sizeof(pointf));
  1351. D[0].x = mid_x(AF) + (B[2].x-B[3].x)/2; //x_center+width
  1352. D[0].y = mid_y(&AF[1]) + (B[2].x-B[3].x)/2;
  1353. D[1].x = D[0].x;
  1354. D[1].y = mid_y(&AF[1]) - (B[2].x-B[3].x)/2; //D[0].y- width
  1355. D[2].x = mid_x(AF) - (B[2].x-B[3].x)/2; //x_center-width
  1356. D[2].y = D[1].y;
  1357. D[3].x = D[2].x;
  1358. D[3].y = D[0].y;
  1359. gvrender_polygon(job, D, sides, filled);
  1360. free(D);
  1361. /*outer square line*/
  1362. C[0].x = mid_x(AF) + (B[2].x-B[3].x)*3/4; //x_center+1.5*width
  1363. C[0].y = mid_y(&AF[1]) + (B[2].x-B[3].x)*3/4; //y_center
  1364. C[1].x = C[0].x;
  1365. C[1].y = mid_y(&AF[1]) - (B[2].x-B[3].x)*3/4; //y_center- 1.5*width
  1366. C[2].x = mid_x(AF) - (B[2].x-B[3].x)*3/4; //x_center-1.5*width
  1367. C[2].y = C[1].y;
  1368. C[3].x = C[2].x;
  1369. C[3].y = C[0].y;
  1370. C[4] = C[0];
  1371. gvrender_polyline(job, C, 5);
  1372. /*dsDNA line right half*/
  1373. C[0].x = mid_x(AF) + (B[2].x-B[3].x)*3/4;
  1374. C[0].y = mid_y(&AF[1]);
  1375. C[1].x = AF[0].x;
  1376. C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
  1377. gvrender_polyline(job, C, 2);
  1378. /*dsDNA line left half*/
  1379. C[0].x = AF[1].x;
  1380. C[0].y = mid_y(&AF[1]);
  1381. C[1].x = mid_x(AF) - (B[2].x-B[3].x)*3/4;
  1382. C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
  1383. gvrender_polyline(job, C, 2);
  1384. break;
  1385. case RIBOSITE:
  1386. /*
  1387. * X with a dashed line on the bottom
  1388. *
  1389. *
  1390. * X
  1391. * ╎
  1392. * ─────┴──────
  1393. */
  1394. //width units are (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
  1395. D = gv_calloc(sides + 12, sizeof(pointf)); // 12-sided x
  1396. D[0].x = mid_x(AF) + (B[2].x-B[3].x)/4; //x_center+width/2 , lower right corner of the x
  1397. D[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)/2; //y_center + width
  1398. D[1].x = D[0].x;
  1399. D[1].y = D[0].y + (B[3].y-B[4].y)/8; //D[0].y +width/4
  1400. D[2].x = D[0].x - (B[2].x-B[3].x)/8; //D[0].x- width/4 //right nook of the x
  1401. D[2].y = D[1].y + (B[3].y-B[4].y)/8; //D[0].y+width/2 or D[1].y+width/4
  1402. D[3].x = D[0].x;
  1403. D[3].y = D[2].y + (B[3].y-B[4].y)/8; //D[2].y + width/4
  1404. D[4].x = D[0].x;
  1405. D[4].y = D[3].y + (B[3].y-B[4].y)/8; //top right corner of the x
  1406. D[5].x = D[2].x;
  1407. D[5].y = D[4].y;
  1408. D[6].x = mid_x(AF);
  1409. D[6].y = D[3].y; //top nook
  1410. D[7].x = D[6].x - (B[2].x-B[3].x)/8; //D[5] mirrored across y
  1411. D[7].y = D[5].y;
  1412. D[8].x = D[7].x - (B[2].x-B[3].x)/8;//top left corner
  1413. D[8].y = D[7].y;
  1414. D[9].x = D[8].x;
  1415. D[9].y = D[3].y;
  1416. D[10].x = D[8].x + (B[2].x-B[3].x)/8;
  1417. D[10].y = D[2].y;
  1418. D[11].x = D[8].x;
  1419. D[11].y = D[1].y;
  1420. D[12].x = D[8].x;
  1421. D[12].y = D[0].y;
  1422. D[13].x = D[10].x;
  1423. D[13].y = D[12].y;
  1424. D[14].x = D[6].x; //bottom nook
  1425. D[14].y = D[1].y;
  1426. D[15].x = D[2].x;
  1427. D[15].y = D[0].y;
  1428. gvrender_polygon(job, D, sides + 12, filled);
  1429. //2-part dash line
  1430. /*line below the x, bottom dash*/
  1431. C[0].x = D[14].x; //x_center
  1432. C[0].y = mid_y(&AF[1]);
  1433. C[1].x = C[0].x;
  1434. C[1].y = C[0].y + (B[3].y-B[4].y)/8; //y_center + 1/4*width
  1435. gvrender_polyline(job, C, 2);
  1436. /*line below the x, top dash*/
  1437. C[0].x = D[14].x; //x_center
  1438. C[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)/4;
  1439. C[1].x = C[0].x;
  1440. C[1].y = C[0].y + (B[3].y-B[4].y)/8;
  1441. gvrender_polyline(job, C, 2);
  1442. /*dsDNA line*/
  1443. C[0].x = AF[1].x;
  1444. C[0].y = mid_y(&AF[1]);
  1445. C[1].x = AF[0].x;
  1446. C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
  1447. gvrender_polyline(job, C, 2);
  1448. free(D);
  1449. break;
  1450. case RNASTAB:
  1451. /*
  1452. * octagon with a dashed line on the bottom
  1453. *
  1454. *
  1455. * O
  1456. * ╎
  1457. * ─────┴──────
  1458. */
  1459. //width units are (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
  1460. D = gv_calloc(sides + 4, sizeof(pointf)); // 12-sided x
  1461. D[0].x = mid_x(AF) + (B[2].x-B[3].x)/8; //x_center+width/8 , lower right corner of the hexagon
  1462. D[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)/2; //y_center + width
  1463. D[1].x = D[0].x + (B[2].x-B[3].x)/8;
  1464. D[1].y = D[0].y + (B[3].y-B[4].y)/8; //D[0].y +width/4
  1465. D[2].x = D[1].x; //D[0].x- width/4
  1466. D[2].y = D[1].y + (B[3].y-B[4].y)/4; //D[1].y+width/2
  1467. D[3].x = D[0].x;
  1468. D[3].y = D[2].y + (B[3].y-B[4].y)/8; //D[2].y + width/4
  1469. D[4].x = D[3].x - (B[2].x-B[3].x)/4;
  1470. D[4].y = D[3].y; //top of the hexagon
  1471. D[5].x = D[4].x - (B[2].x-B[3].x)/8;
  1472. D[5].y = D[2].y;
  1473. D[6].x = D[5].x;
  1474. D[6].y = D[1].y; //left side
  1475. D[7].x = D[4].x;
  1476. D[7].y = D[0].y; //bottom
  1477. gvrender_polygon(job, D, sides + 4, filled);
  1478. //2-part dash line
  1479. /*line below the x, bottom dash*/
  1480. C[0].x = mid_x(AF);
  1481. C[0].y = mid_y(&AF[1]);
  1482. C[1].x = C[0].x;
  1483. C[1].y = C[0].y + (B[3].y-B[4].y)/8; //y_center + 1/4*width
  1484. gvrender_polyline(job, C, 2);
  1485. /*line below the x, top dash*/
  1486. C[0].x = mid_x(AF);
  1487. C[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)/4;
  1488. C[1].x = C[0].x;
  1489. C[1].y = C[0].y + (B[3].y-B[4].y)/8;
  1490. gvrender_polyline(job, C, 2);
  1491. /*dsDNA line*/
  1492. C[0].x = AF[1].x;
  1493. C[0].y = mid_y(&AF[1]);
  1494. C[1].x = AF[0].x;
  1495. C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
  1496. gvrender_polyline(job, C, 2);
  1497. free(D);
  1498. break;
  1499. case PROTEASESITE:
  1500. /*
  1501. * X with a solid line on the bottom
  1502. *
  1503. *
  1504. * X
  1505. * │
  1506. * ─────┴──────
  1507. */
  1508. //width units are (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
  1509. D = gv_calloc(sides + 12, sizeof(pointf)); // 12-sided x
  1510. D[0].x = mid_x(AF) + (B[2].x-B[3].x)/4; //x_center+width/2 , lower right corner of the x
  1511. D[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)/2; //y_center + width
  1512. D[1].x = D[0].x;
  1513. D[1].y = D[0].y + (B[3].y-B[4].y)/8; //D[0].y +width/4
  1514. D[2].x = D[0].x - (B[2].x-B[3].x)/8; //D[0].x- width/4 //right nook of the x
  1515. D[2].y = D[1].y + (B[3].y-B[4].y)/8; //D[0].y+width/2 or D[1].y+width/4
  1516. D[3].x = D[0].x;
  1517. D[3].y = D[2].y + (B[3].y-B[4].y)/8; //D[2].y + width/4
  1518. D[4].x = D[0].x;
  1519. D[4].y = D[3].y + (B[3].y-B[4].y)/8; //top right corner of the x
  1520. D[5].x = D[2].x;
  1521. D[5].y = D[4].y;
  1522. D[6].x = mid_x(AF);
  1523. D[6].y = D[3].y; //top nook
  1524. D[7].x = D[6].x - (B[2].x-B[3].x)/8; //D[5] mirrored across y
  1525. D[7].y = D[5].y;
  1526. D[8].x = D[7].x - (B[2].x-B[3].x)/8;//top left corner
  1527. D[8].y = D[7].y;
  1528. D[9].x = D[8].x;
  1529. D[9].y = D[3].y;
  1530. D[10].x = D[8].x + (B[2].x-B[3].x)/8;
  1531. D[10].y = D[2].y;
  1532. D[11].x = D[8].x;
  1533. D[11].y = D[1].y;
  1534. D[12].x = D[8].x;
  1535. D[12].y = D[0].y;
  1536. D[13].x = D[10].x;
  1537. D[13].y = D[12].y;
  1538. D[14].x = D[6].x; //bottom nook
  1539. D[14].y = D[1].y;
  1540. D[15].x = D[2].x;
  1541. D[15].y = D[0].y;
  1542. gvrender_polygon(job, D, sides + 12, filled);
  1543. /*line below the x*/
  1544. C[0] = D[14];
  1545. C[1].x = C[0].x;
  1546. C[1].y = mid_y(&AF[1]);
  1547. gvrender_polyline(job, C, 2);
  1548. /*dsDNA line*/
  1549. C[0].x = AF[1].x;
  1550. C[0].y = mid_y(&AF[1]);
  1551. C[1].x = AF[0].x;
  1552. C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
  1553. gvrender_polyline(job, C, 2);
  1554. free(D);
  1555. break;
  1556. case PROTEINSTAB:
  1557. /*
  1558. * octagon with a solid line on the bottom
  1559. *
  1560. *
  1561. * O
  1562. * │
  1563. * ─────┴──────
  1564. */
  1565. //width units are (B[2].x-B[3].x)/2 or (B[3].y-B[4].y)/2;
  1566. D = gv_calloc(sides + 4, sizeof(pointf)); // 12-sided x
  1567. D[0].x = mid_x(AF) + (B[2].x-B[3].x)/8; //x_center+width/8 , lower right corner of the hexagon
  1568. D[0].y = mid_y(&AF[1]) + (B[3].y-B[4].y)/2; //y_center + width
  1569. D[1].x = D[0].x + (B[2].x-B[3].x)/8;
  1570. D[1].y = D[0].y + (B[3].y-B[4].y)/8; //D[0].y +width/4
  1571. D[2].x = D[1].x; //D[0].x- width/4
  1572. D[2].y = D[1].y + (B[3].y-B[4].y)/4; //D[1].y+width/2
  1573. D[3].x = D[0].x;
  1574. D[3].y = D[2].y + (B[3].y-B[4].y)/8; //D[2].y + width/4
  1575. D[4].x = D[3].x - (B[2].x-B[3].x)/4;
  1576. D[4].y = D[3].y; //top of the hexagon
  1577. D[5].x = D[4].x - (B[2].x-B[3].x)/8;
  1578. D[5].y = D[2].y;
  1579. D[6].x = D[5].x;
  1580. D[6].y = D[1].y; //left side
  1581. D[7].x = D[4].x;
  1582. D[7].y = D[0].y; //bottom
  1583. gvrender_polygon(job, D, sides + 4, filled);
  1584. /*line below the x*/
  1585. C[0].x = mid_x(AF);
  1586. C[0].y = D[0].y;
  1587. C[1].x = C[0].x;
  1588. C[1].y = mid_y(&AF[1]);
  1589. gvrender_polyline(job, C, 2);
  1590. /*dsDNA line*/
  1591. C[0].x = AF[1].x;
  1592. C[0].y = mid_y(&AF[1]);
  1593. C[1].x = AF[0].x;
  1594. C[1].y = AF[2].y + (AF[0].y - AF[3].y)/2;
  1595. gvrender_polyline(job, C, 2);
  1596. free(D);
  1597. break;
  1598. case RPROMOTER:
  1599. /*
  1600. * Adjust the perimeter for the protrusions.
  1601. *
  1602. *
  1603. * D[1] = AF[1] │╲
  1604. * ×────────────────× ╲
  1605. * │ D[0] ╲
  1606. * │ ╲
  1607. * │ ╱
  1608. * │ ╱
  1609. * │ ┌───────┐ ╱
  1610. * │ │ │╱
  1611. * └────────┘
  1612. */
  1613. /* Add the tab edges. */
  1614. D = gv_calloc(sides + 5, sizeof(pointf)); // 5 new points
  1615. D[0].x = B[1].x - (B[2].x - B[3].x)/2;
  1616. D[0].y = B[1].y - (B[3].y - B[4].y)/2;
  1617. D[1].x = B[3].x;
  1618. D[1].y = B[3].y - (B[3].y - B[4].y)/2;
  1619. D[2].x = AF[2].x;
  1620. D[2].y = AF[2].y;
  1621. D[3].x = B[2].x + (B[2].x - B[3].x)/2;
  1622. D[3].y = AF[2].y;
  1623. D[4].x = B[2].x + (B[2].x - B[3].x)/2;
  1624. D[4].y = AF[2].y + (B[3].y - B[4].y)/2;
  1625. D[5].x = B[1].x - (B[2].x - B[3].x)/2;
  1626. D[5].y = AF[2].y + (B[3].y - B[4].y)/2;
  1627. D[6].x = B[1].x - (B[2].x - B[3].x)/2;
  1628. D[6].y = AF[3].y;
  1629. D[7].y = AF[0].y - (AF[0].y - AF[3].y)/2; /*triangle point */
  1630. D[7].x = AF[0].x; /*triangle point */
  1631. D[8].y = AF[0].y;
  1632. D[8].x = B[1].x - (B[2].x - B[3].x)/2;
  1633. gvrender_polygon(job, D, sides + 5, filled);
  1634. free(D);
  1635. break;
  1636. case RARROW:
  1637. /*
  1638. * Adjust the perimeter for the protrusions.
  1639. *
  1640. *
  1641. * D[1] = AF[1] │╲
  1642. * ×────────────────× ╲
  1643. * │ D[0] ╲
  1644. * │ ╲
  1645. * │ ╱
  1646. * │ ╱
  1647. * └────────────────┐ ╱
  1648. * │╱
  1649. *
  1650. */
  1651. /* Add the tab edges. */
  1652. D = gv_calloc(sides + 3, sizeof(pointf)); // 3 new points
  1653. D[0].x = B[1].x - (B[2].x - B[3].x)/2;
  1654. D[0].y = B[1].y - (B[3].y - B[4].y)/2;
  1655. D[1].x = B[3].x;
  1656. D[1].y = B[3].y - (B[3].y - B[4].y)/2;
  1657. D[2].x = AF[2].x;
  1658. D[2].y = AF[2].y + (B[3].y - B[4].y)/2;
  1659. D[3].x = B[1].x - (B[2].x - B[3].x)/2;
  1660. D[3].y = AF[2].y + (B[3].y - B[4].y)/2;
  1661. D[4].x = B[1].x - (B[2].x - B[3].x)/2;
  1662. D[4].y = AF[3].y;
  1663. D[5].y = AF[0].y - (AF[0].y - AF[3].y)/2;/*triangle point*/
  1664. D[5].x = AF[0].x; /*triangle point */
  1665. D[6].y = AF[0].y;
  1666. D[6].x = B[1].x - (B[2].x - B[3].x)/2;
  1667. gvrender_polygon(job, D, sides + 3, filled);
  1668. free(D);
  1669. break;
  1670. case LARROW:
  1671. /*
  1672. * Adjust the perimeter for the protrusions.
  1673. *
  1674. *
  1675. * ╱│
  1676. * ╱ └────────────────┐
  1677. * ╱ │
  1678. * ╲ │
  1679. * ╲ ┌────────────────┘
  1680. * ╲│
  1681. *
  1682. */
  1683. /* Add the tab edges. */
  1684. D = gv_calloc(sides + 3, sizeof(pointf)); // 3 new points
  1685. D[0].x = AF[0].x;
  1686. D[0].y = AF[0].y - (B[3].y-B[4].y)/2;
  1687. D[1].x = B[2].x + (B[2].x - B[3].x)/2;
  1688. D[1].y = AF[0].y - (B[3].y-B[4].y)/2;/*D[0].y*/
  1689. D[2].x = B[2].x + (B[2].x - B[3].x)/2;/*D[1].x*/
  1690. D[2].y = B[2].y;
  1691. D[3].x = AF[1].x; /*triangle point*/
  1692. D[3].y = AF[1].y - (AF[1].y - AF[2].y)/2; /*triangle point*/
  1693. D[4].x = B[2].x + (B[2].x - B[3].x)/2;/*D[1].x*/
  1694. D[4].y = AF[2].y;
  1695. D[5].y = AF[2].y + (B[3].y-B[4].y)/2;
  1696. D[5].x = B[2].x + (B[2].x - B[3].x)/2;/*D[1].x*/
  1697. D[6].y = AF[3].y + (B[3].y - B[4].y)/2;
  1698. D[6].x = AF[0].x;/*D[0]*/
  1699. gvrender_polygon(job, D, sides + 3, filled);
  1700. free(D);
  1701. break;
  1702. case LPROMOTER:
  1703. /*
  1704. * Adjust the perimeter for the protrusions.
  1705. *
  1706. *
  1707. * ╱│
  1708. * ╱ └────────────────×
  1709. * ╱ D[0]
  1710. * ╱ │
  1711. * ╲ │
  1712. * ╲ │
  1713. * ╲ ┌────────┐ │
  1714. * ╲│ │ │
  1715. * └───────┘
  1716. */
  1717. /* Add the tab edges. */
  1718. D = gv_calloc(sides + 5, sizeof(pointf)); // 3 new points
  1719. D[0].x = AF[0].x;
  1720. D[0].y = AF[0].y - (B[3].y-B[4].y)/2;
  1721. D[1].x = B[2].x + (B[2].x - B[3].x)/2;
  1722. D[1].y = AF[0].y - (B[3].y-B[4].y)/2;/*D[0].y*/
  1723. D[2].x = B[2].x + (B[2].x - B[3].x)/2;/*D[1].x*/
  1724. D[2].y = B[2].y;
  1725. D[3].x = AF[1].x; /*triangle point*/
  1726. D[3].y = AF[1].y - (AF[1].y - AF[2].y)/2; /*triangle point*/
  1727. D[4].x = B[2].x + (B[2].x - B[3].x)/2;/*D[1].x*/
  1728. D[4].y = AF[2].y;
  1729. D[5].y = AF[2].y + (B[3].y-B[4].y)/2;
  1730. D[5].x = B[2].x + (B[2].x - B[3].x)/2;/*D[1].x*/
  1731. D[6].y = AF[3].y + (B[3].y - B[4].y)/2;
  1732. D[6].x = B[1].x - (B[2].x - B[3].x)/2;
  1733. D[7].x = B[1].x - (B[2].x - B[3].x)/2;/*D[6].x*/
  1734. D[7].y = AF[3].y;
  1735. D[8].x = AF[3].x;
  1736. D[8].y = AF[3].y;
  1737. gvrender_polygon(job, D, sides + 5, filled);
  1738. free(D);
  1739. break;
  1740. }
  1741. free(B);
  1742. }
  1743. /*=============================poly start=========================*/
  1744. /* userSize;
  1745. * Return maximum size, in points, of width and height supplied
  1746. * by user, if any. Return 0 otherwise.
  1747. */
  1748. static double userSize(node_t * n)
  1749. {
  1750. double w, h;
  1751. w = late_double(n, N_width, 0.0, MIN_NODEWIDTH);
  1752. h = late_double(n, N_height, 0.0, MIN_NODEHEIGHT);
  1753. return INCH2PS(fmax(w, h));
  1754. }
  1755. shape_kind shapeOf(node_t * n)
  1756. {
  1757. shape_desc *sh = ND_shape(n);
  1758. void (*ifn) (node_t *);
  1759. if (!sh)
  1760. return SH_UNSET;
  1761. ifn = ND_shape(n)->fns->initfn;
  1762. if (ifn == poly_init)
  1763. return SH_POLY;
  1764. else if (ifn == record_init)
  1765. return SH_RECORD;
  1766. else if (ifn == point_init)
  1767. return SH_POINT;
  1768. else if (ifn == epsf_init)
  1769. return SH_EPSF;
  1770. else
  1771. return SH_UNSET;
  1772. }
  1773. bool isPolygon(node_t * n)
  1774. {
  1775. return ND_shape(n) && ND_shape(n)->fns->initfn == poly_init;
  1776. }
  1777. static void poly_init(node_t * n)
  1778. {
  1779. pointf dimen, min_bb;
  1780. pointf outline_bb;
  1781. point imagesize;
  1782. pointf *vertices;
  1783. char *p, *sfile, *fxd;
  1784. double temp, alpha, beta, gamma;
  1785. double orientation, distortion, skew;
  1786. double scalex, scaley;
  1787. double width, height, marginx, marginy, spacex;
  1788. polygon_t *poly = gv_alloc(sizeof(polygon_t));
  1789. bool isPlain = IS_PLAIN(n);
  1790. bool regular = !!ND_shape(n)->polygon->regular;
  1791. size_t peripheries = ND_shape(n)->polygon->peripheries;
  1792. size_t sides = ND_shape(n)->polygon->sides;
  1793. orientation = ND_shape(n)->polygon->orientation;
  1794. skew = ND_shape(n)->polygon->skew;
  1795. distortion = ND_shape(n)->polygon->distortion;
  1796. regular |= mapbool(agget(n, "regular"));
  1797. /* all calculations in floating point POINTS */
  1798. /* make x and y dimensions equal if node is regular
  1799. * If the user has specified either width or height, use the max.
  1800. * Else use minimum default value.
  1801. * If node is not regular, use the current width and height.
  1802. */
  1803. if (isPlain) {
  1804. width = height = 0;
  1805. }
  1806. else if (regular) {
  1807. double sz = userSize(n);
  1808. if (sz > 0.0)
  1809. width = height = sz;
  1810. else {
  1811. width = ND_width(n);
  1812. height = ND_height(n);
  1813. width = height = INCH2PS(fmin(width, height));
  1814. }
  1815. } else {
  1816. width = INCH2PS(ND_width(n));
  1817. height = INCH2PS(ND_height(n));
  1818. }
  1819. peripheries = (size_t)late_int(n, N_peripheries, (int)peripheries, 0);
  1820. orientation += late_double(n, N_orientation, 0.0, -360.0);
  1821. if (sides == 0) { /* not for builtins */
  1822. skew = late_double(n, N_skew, 0.0, -100.0);
  1823. sides = (size_t)late_int(n, N_sides, 4, 0);
  1824. distortion = late_double(n, N_distortion, 0.0, -100.0);
  1825. }
  1826. /* get label dimensions */
  1827. dimen = ND_label(n)->dimen;
  1828. /* minimal whitespace around label */
  1829. if (dimen.x > 0 || dimen.y > 0) {
  1830. /* padding */
  1831. if (!isPlain) {
  1832. if ((p = agget(n, "margin"))) {
  1833. marginx = marginy = 0;
  1834. const int i = sscanf(p, "%lf,%lf", &marginx, &marginy);
  1835. marginx = fmax(marginx, 0);
  1836. marginy = fmax(marginy, 0);
  1837. if (i > 0) {
  1838. dimen.x += 2 * INCH2PS(marginx);
  1839. if (i > 1)
  1840. dimen.y += 2 * INCH2PS(marginy);
  1841. else
  1842. dimen.y += 2 * INCH2PS(marginx);
  1843. } else
  1844. PAD(dimen);
  1845. } else
  1846. PAD(dimen);
  1847. }
  1848. }
  1849. spacex = dimen.x - ND_label(n)->dimen.x;
  1850. /* quantization */
  1851. if ((temp = GD_drawing(agraphof(n))->quantum) > 0.0) {
  1852. temp = INCH2PS(temp);
  1853. dimen.x = quant(dimen.x, temp);
  1854. dimen.y = quant(dimen.y, temp);
  1855. }
  1856. imagesize.x = imagesize.y = 0;
  1857. if (ND_shape(n)->usershape) {
  1858. /* custom requires a shapefile
  1859. * not custom is an adaptable user shape such as a postscript
  1860. * function.
  1861. */
  1862. if (streq(ND_shape(n)->name, "custom")) {
  1863. sfile = agget(n, "shapefile");
  1864. imagesize = gvusershape_size(agraphof(n), sfile);
  1865. if (imagesize.x == -1 && imagesize.y == -1) {
  1866. agwarningf(
  1867. "No or improper shapefile=\"%s\" for node \"%s\"\n",
  1868. sfile ? sfile : "<nil>", agnameof(n));
  1869. imagesize.x = imagesize.y = 0;
  1870. } else {
  1871. GD_has_images(agraphof(n)) = true;
  1872. imagesize.x += 2; /* some fixed padding */
  1873. imagesize.y += 2;
  1874. }
  1875. }
  1876. } else if ((sfile = agget(n, "image")) && *sfile != '\0') {
  1877. imagesize = gvusershape_size(agraphof(n), sfile);
  1878. if (imagesize.x == -1 && imagesize.y == -1) {
  1879. agwarningf(
  1880. "No or improper image=\"%s\" for node \"%s\"\n",
  1881. sfile ? sfile : "<nil>", agnameof(n));
  1882. imagesize.x = imagesize.y = 0;
  1883. } else {
  1884. GD_has_images(agraphof(n)) = true;
  1885. imagesize.x += 2; /* some fixed padding */
  1886. imagesize.y += 2;
  1887. }
  1888. }
  1889. /* initialize node bb to labelsize */
  1890. pointf bb = {.x = fmax(dimen.x, imagesize.x),
  1891. .y = fmax(dimen.y, imagesize.y)};
  1892. /* I don't know how to distort or skew ellipses in postscript */
  1893. /* Convert request to a polygon with a large number of sides */
  1894. if (sides <= 2 &&
  1895. (!is_exactly_zero(distortion) || !is_exactly_zero(skew))) {
  1896. sides = 120;
  1897. }
  1898. /* extra sizing depends on if label is centered vertically */
  1899. p = agget(n, "labelloc");
  1900. if (p && (p[0] == 't' || p[0] == 'b'))
  1901. ND_label(n)->valign = p[0];
  1902. else
  1903. ND_label(n)->valign = 'c';
  1904. const bool isBox = sides == 4 && fabs(fmod(orientation, 90)) < 0.5
  1905. && is_exactly_zero(distortion) && is_exactly_zero(skew);
  1906. if (isBox) {
  1907. /* for regular boxes the fit should be exact */
  1908. } else if (ND_shape(n)->polygon->vertices) {
  1909. poly_desc_t* pd = (poly_desc_t*)ND_shape(n)->polygon->vertices;
  1910. bb = pd->size_gen(bb);
  1911. } else {
  1912. /* for all other shapes, compute a smallest ellipse
  1913. * containing bb centered on the origin, and then pad for that.
  1914. * We assume the ellipse is defined by a scaling up of bb.
  1915. */
  1916. temp = bb.y * SQRT2;
  1917. if (height > temp && ND_label(n)->valign == 'c') {
  1918. /* if there is height to spare
  1919. * and the label is centered vertically
  1920. * then just pad x in proportion to the spare height */
  1921. bb.x *= sqrt(1. / (1. - SQR(bb.y / height)));
  1922. } else {
  1923. bb.x *= SQRT2;
  1924. bb.y = temp;
  1925. }
  1926. #if 1
  1927. if (sides > 2) {
  1928. temp = cos(M_PI / (double)sides);
  1929. bb.x /= temp;
  1930. bb.y /= temp;
  1931. /* FIXME - for odd-sided polygons, e.g. triangles, there
  1932. would be a better fit with some vertical adjustment of the shape */
  1933. }
  1934. #endif
  1935. }
  1936. /* at this point, bb is the minimum size of node that can hold the label */
  1937. min_bb = bb;
  1938. /* increase node size to width/height if needed */
  1939. fxd = late_string(n, N_fixed, "false");
  1940. if (*fxd == 's' && streq(fxd,"shape")) {
  1941. bb = (pointf){.x = width, .y = height};
  1942. poly->option.fixedshape = true;
  1943. } else if (mapbool(fxd)) {
  1944. /* check only label, as images we can scale to fit */
  1945. if (width < ND_label(n)->dimen.x || height < ND_label(n)->dimen.y)
  1946. agwarningf(
  1947. "node '%s', graph '%s' size too small for label\n",
  1948. agnameof(n), agnameof(agraphof(n)));
  1949. bb = (pointf){.x = width, .y = height};
  1950. } else {
  1951. bb.x = width = fmax(width, bb.x);
  1952. bb.y = height = fmax(height, bb.y);
  1953. }
  1954. /* If regular, make dimensions the same.
  1955. * Need this to guarantee final node size is regular.
  1956. */
  1957. if (regular) {
  1958. width = height = bb.x = bb.y = fmax(bb.x, bb.y);
  1959. }
  1960. /* Compute space available for label. Provides the justification borders */
  1961. if (!mapbool(late_string(n, N_nojustify, "false"))) {
  1962. if (isBox) {
  1963. ND_label(n)->space.x = fmax(dimen.x, bb.x) - spacex;
  1964. }
  1965. else if (dimen.y < bb.y) {
  1966. temp = bb.x * sqrt(1.0 - SQR(dimen.y) / SQR(bb.y));
  1967. ND_label(n)->space.x = fmax(dimen.x, temp) - spacex;
  1968. }
  1969. else
  1970. ND_label(n)->space.x = dimen.x - spacex;
  1971. } else {
  1972. ND_label(n)->space.x = dimen.x - spacex;
  1973. }
  1974. if (!poly->option.fixedshape) {
  1975. temp = bb.y - min_bb.y;
  1976. if (dimen.y < imagesize.y)
  1977. temp += imagesize.y - dimen.y;
  1978. ND_label(n)->space.y = dimen.y + temp;
  1979. }
  1980. const double penwidth = late_int(n, N_penwidth, DEFAULT_NODEPENWIDTH, MIN_NODEPENWIDTH);
  1981. size_t outp = peripheries;
  1982. if (peripheries < 1)
  1983. outp = 1;
  1984. if (peripheries >= 1 && penwidth > 0) {
  1985. // allocate extra vertices representing the outline, i.e., the outermost
  1986. // periphery with penwidth taken into account
  1987. ++outp;
  1988. }
  1989. if (sides < 3) { /* ellipses */
  1990. sides = 2;
  1991. vertices = gv_calloc(outp * sides, sizeof(pointf));
  1992. pointf P = {.x = bb.x / 2., .y = bb.y / 2.};
  1993. vertices[0] = (pointf){.x = -P.x, .y = -P.y};
  1994. vertices[1] = P;
  1995. if (peripheries > 1) {
  1996. for (size_t j = 1, i = 2; j < peripheries; j++) {
  1997. P.x += GAP;
  1998. P.y += GAP;
  1999. vertices[i] = (pointf){.x = -P.x, .y = -P.y};
  2000. i++;
  2001. vertices[i] = P;
  2002. i++;
  2003. }
  2004. bb.x = 2. * P.x;
  2005. bb.y = 2. * P.y;
  2006. }
  2007. outline_bb = bb;
  2008. if (outp > peripheries) {
  2009. // add an outline at half the penwidth outside the outermost periphery
  2010. P.x += penwidth / 2;
  2011. P.y += penwidth / 2;
  2012. size_t i = sides * peripheries;
  2013. vertices[i] = (pointf){.x = -P.x, .y = -P.y};
  2014. i++;
  2015. vertices[i] = P;
  2016. i++;
  2017. outline_bb.x = 2. * P.x;
  2018. outline_bb.y = 2. * P.y;
  2019. }
  2020. } else {
  2021. /*
  2022. * FIXME - this code is wrong - it doesn't work for concave boundaries.
  2023. * (e.g. "folder" or "promoter")
  2024. * I don't think it even needs sectorangle, or knowledge of skewed shapes.
  2025. * (Concepts that only work for convex regular (modulo skew/distort) polygons.)
  2026. *
  2027. * I think it only needs to know inside v. outside (by always drawing
  2028. * boundaries clockwise, say), and the two adjacent segments.
  2029. *
  2030. * It needs to find the point where the two lines, parallel to
  2031. * the current segments, and outside by GAP distance, intersect.
  2032. */
  2033. double sinx = 0, cosx = 0, xmax, ymax;
  2034. vertices = gv_calloc(outp * sides, sizeof(pointf));
  2035. if (ND_shape(n)->polygon->vertices) {
  2036. poly_desc_t* pd = (poly_desc_t*)ND_shape(n)->polygon->vertices;
  2037. pd->vertex_gen (vertices, &bb);
  2038. xmax = bb.x/2;
  2039. ymax = bb.y/2;
  2040. } else {
  2041. double angle, sectorangle, sidelength, skewdist, gdistortion, gskew;
  2042. sectorangle = 2. * M_PI / (double)sides;
  2043. sidelength = sin(sectorangle / 2.);
  2044. skewdist = hypot(fabs(distortion) + fabs(skew), 1.);
  2045. gdistortion = distortion * SQRT2 / cos(sectorangle / 2.);
  2046. gskew = skew / 2.;
  2047. angle = (sectorangle - M_PI) / 2.;
  2048. sinx = sin(angle);
  2049. cosx = cos(angle);
  2050. pointf R = {.x = .5 * cosx, .y = .5 * sinx};
  2051. xmax = ymax = 0.;
  2052. angle += (M_PI - sectorangle) / 2.;
  2053. for (size_t i = 0; i < sides; i++) {
  2054. /*next regular vertex */
  2055. angle += sectorangle;
  2056. sinx = sin(angle);
  2057. cosx = cos(angle);
  2058. R.x += sidelength * cosx;
  2059. R.y += sidelength * sinx;
  2060. /*distort and skew */
  2061. pointf P = {
  2062. .x = R.x * (skewdist + R.y * gdistortion) + R.y * gskew,
  2063. .y = R.y};
  2064. /*orient P.x,P.y */
  2065. alpha = RADIANS(orientation) + atan2(P.y, P.x);
  2066. sinx = sin(alpha);
  2067. cosx = cos(alpha);
  2068. P.x = P.y = hypot(P.x, P.y);
  2069. P.x *= cosx;
  2070. P.y *= sinx;
  2071. /*scale for label */
  2072. P.x *= bb.x;
  2073. P.y *= bb.y;
  2074. /*find max for bounding box */
  2075. xmax = fmax(fabs(P.x), xmax);
  2076. ymax = fmax(fabs(P.y), ymax);
  2077. /* store result in array of points */
  2078. vertices[i] = P;
  2079. if (isBox) { /* enforce exact symmetry of box */
  2080. vertices[1] = (pointf){.x = -P.x, .y = P.y};
  2081. vertices[2] = (pointf){.x = -P.x, .y = -P.y};
  2082. vertices[3] = (pointf){.x = P.x, .y = -P.y};
  2083. break;
  2084. }
  2085. }
  2086. }
  2087. /* apply minimum dimensions */
  2088. xmax *= 2.;
  2089. ymax *= 2.;
  2090. bb = (pointf){.x = fmax(width, xmax), .y = fmax(height, ymax)};
  2091. outline_bb = bb;
  2092. scalex = bb.x / xmax;
  2093. scaley = bb.y / ymax;
  2094. size_t i;
  2095. for (i = 0; i < sides; i++) {
  2096. pointf P = vertices[i];
  2097. P.x *= scalex;
  2098. P.y *= scaley;
  2099. vertices[i] = P;
  2100. }
  2101. if (outp > 1) {
  2102. pointf R = vertices[0];
  2103. pointf Q;
  2104. for (size_t j = 1; j < sides; j++) {
  2105. Q = vertices[(i - j) % sides];
  2106. if (!is_exactly_equal(Q.x, R.x) || !is_exactly_equal(Q.y, R.y)) {
  2107. break;
  2108. }
  2109. }
  2110. assert(!is_exactly_equal(R.x, Q.x) || !is_exactly_equal(R.y, Q.y));
  2111. beta = atan2(R.y - Q.y, R.x - Q.x);
  2112. pointf Qprev = Q;
  2113. for (i = 0; i < sides; i++) {
  2114. /*for each vertex find the bisector */
  2115. Q = vertices[i];
  2116. if (is_exactly_equal(Q.x, Qprev.x) && is_exactly_equal(Q.y, Qprev.y)) {
  2117. // The vertex points for the side ending at Q are equal,
  2118. // i.e. this side is actually a point and its angle is
  2119. // undefined. Therefore we keep the same offset for the end
  2120. // point as already calculated for the start point. This may
  2121. // occur for shapes which are represented as polygons during
  2122. // layout, but are drawn using bezier curves during
  2123. // rendering, e.g. for the `cylinder` shape.
  2124. } else {
  2125. for (size_t j = 1; j < sides; j++) {
  2126. R = vertices[(i + j) % sides];
  2127. if (!is_exactly_equal(R.x, Q.x) || !is_exactly_equal(R.y, Q.y)) {
  2128. break;
  2129. }
  2130. }
  2131. assert(!is_exactly_equal(R.x, Q.x) || !is_exactly_equal(R.y, Q.y));
  2132. alpha = beta;
  2133. beta = atan2(R.y - Q.y, R.x - Q.x);
  2134. gamma = (alpha + M_PI - beta) / 2.;
  2135. /*find distance along bisector to */
  2136. /*intersection of next periphery */
  2137. temp = GAP / sin(gamma);
  2138. /*convert this distance to x and y */
  2139. sinx = sin(alpha - gamma) * temp;
  2140. cosx = cos(alpha - gamma) * temp;
  2141. }
  2142. assert(cosx != 0 || sinx != 0);
  2143. Qprev = Q;
  2144. /*save the vertices of all the */
  2145. /*peripheries at this base vertex */
  2146. for (size_t j = 1; j < peripheries; j++) {
  2147. Q.x += cosx;
  2148. Q.y += sinx;
  2149. vertices[i + j * sides] = Q;
  2150. }
  2151. if (outp > peripheries) {
  2152. // add an outline at half the penwidth outside the outermost periphery
  2153. Q.x += cosx * penwidth / 2 / GAP;
  2154. Q.y += sinx * penwidth / 2 / GAP;
  2155. vertices[i + peripheries * sides] = Q;
  2156. }
  2157. }
  2158. for (i = 0; i < sides; i++) {
  2159. pointf P = vertices[i + (peripheries - 1) * sides];
  2160. bb = (pointf){.x = fmax(2.0 * fabs(P.x), bb.x),
  2161. .y = fmax(2.0 * fabs(P.y), bb.y)};
  2162. Q = vertices[i + (outp - 1) * sides];
  2163. outline_bb = (pointf){.x = fmax(2.0 * fabs(Q.x), outline_bb.x),
  2164. .y = fmax(2.0 * fabs(Q.y), outline_bb.y)};
  2165. }
  2166. }
  2167. }
  2168. poly->regular = regular;
  2169. poly->peripheries = peripheries;
  2170. poly->sides = sides;
  2171. poly->orientation = orientation;
  2172. poly->skew = skew;
  2173. poly->distortion = distortion;
  2174. poly->vertices = vertices;
  2175. if (poly->option.fixedshape) {
  2176. /* set width and height to reflect label and shape */
  2177. ND_width(n) = PS2INCH(fmax(dimen.x, bb.x));
  2178. ND_height(n) = PS2INCH(fmax(dimen.y, bb.y));
  2179. ND_outline_width(n) = PS2INCH(fmax(dimen.x, outline_bb.x));
  2180. ND_outline_height(n) = PS2INCH(fmax(dimen.y, outline_bb.y));
  2181. } else {
  2182. ND_width(n) = PS2INCH(bb.x);
  2183. ND_height(n) = PS2INCH(bb.y);
  2184. ND_outline_width(n) = PS2INCH(outline_bb.x);
  2185. ND_outline_height(n) = PS2INCH(outline_bb.y);
  2186. }
  2187. ND_shape_info(n) = poly;
  2188. }
  2189. static void poly_free(node_t * n)
  2190. {
  2191. polygon_t *p = ND_shape_info(n);
  2192. if (p) {
  2193. free(p->vertices);
  2194. free(p);
  2195. }
  2196. }
  2197. /* poly_inside:
  2198. * Return true if point p is inside polygonal shape of node inside_context->s.n.
  2199. * Calculations are done using unrotated node shape. Thus, if p is in a rotated
  2200. * coordinate system, it is reset as P in the unrotated coordinate system. Similarly,
  2201. * the ND_rw, ND_lw and ND_ht values are rotated if the graph is flipped.
  2202. */
  2203. static bool poly_inside(inside_t * inside_context, pointf p)
  2204. {
  2205. size_t sides;
  2206. const pointf O = {0};
  2207. pointf *vertex = NULL;
  2208. int s;
  2209. pointf P, Q, R;
  2210. boxf *bp;
  2211. node_t *n;
  2212. if (!inside_context) {
  2213. return false;
  2214. }
  2215. bp = inside_context->s.bp;
  2216. n = inside_context->s.n;
  2217. P = ccwrotatepf(p, 90 * GD_rankdir(agraphof(n)));
  2218. /* Quick test if port rectangle is target */
  2219. if (bp) {
  2220. boxf bbox = *bp;
  2221. return INSIDE(P, bbox);
  2222. }
  2223. if (n != inside_context->s.lastn) {
  2224. double n_width, n_height;
  2225. double n_outline_width;
  2226. double n_outline_height;
  2227. inside_context->s.last_poly = ND_shape_info(n);
  2228. vertex = inside_context->s.last_poly->vertices;
  2229. sides = inside_context->s.last_poly->sides;
  2230. double xsize, ysize;
  2231. if (inside_context->s.last_poly->option.fixedshape) {
  2232. boxf bb = polyBB(inside_context->s.last_poly);
  2233. n_width = bb.UR.x - bb.LL.x;
  2234. n_height = bb.UR.y - bb.LL.y;
  2235. n_outline_width = n_width;
  2236. n_outline_height = n_height;
  2237. /* get point and node size adjusted for rankdir=LR */
  2238. if (GD_flip(agraphof(n))) {
  2239. ysize = n_width;
  2240. xsize = n_height;
  2241. } else {
  2242. xsize = n_width;
  2243. ysize = n_height;
  2244. }
  2245. } else {
  2246. /* get point and node size adjusted for rankdir=LR */
  2247. if (GD_flip(agraphof(n))) {
  2248. ysize = ND_lw(n) + ND_rw(n);
  2249. xsize = ND_ht(n);
  2250. } else {
  2251. xsize = ND_lw(n) + ND_rw(n);
  2252. ysize = ND_ht(n);
  2253. }
  2254. n_width = INCH2PS(ND_width(n));
  2255. n_height = INCH2PS(ND_height(n));
  2256. n_outline_width = INCH2PS(ND_outline_width(n));
  2257. n_outline_height = INCH2PS(ND_outline_height(n));
  2258. }
  2259. /* scale */
  2260. inside_context->s.scalex = n_width;
  2261. if (!is_exactly_zero(xsize))
  2262. inside_context->s.scalex /= xsize;
  2263. inside_context->s.scaley = n_height;
  2264. if (!is_exactly_zero(ysize))
  2265. inside_context->s.scaley /= ysize;
  2266. inside_context->s.box_URx = n_outline_width / 2;
  2267. inside_context->s.box_URy = n_outline_height / 2;
  2268. const double penwidth = late_int(n, N_penwidth, DEFAULT_NODEPENWIDTH, MIN_NODEPENWIDTH);
  2269. if (inside_context->s.last_poly->peripheries >= 1 && penwidth > 0) {
  2270. /* index to outline, i.e., the outer-periphery with penwidth taken into account */
  2271. inside_context->s.outp =
  2272. (inside_context->s.last_poly->peripheries + 1 - 1) * sides;
  2273. } else if (inside_context->s.last_poly->peripheries < 1) {
  2274. inside_context->s.outp = 0;
  2275. } else {
  2276. /* index to outer-periphery */
  2277. inside_context->s.outp =
  2278. (inside_context->s.last_poly->peripheries - 1) * sides;
  2279. }
  2280. inside_context->s.lastn = n;
  2281. } else {
  2282. vertex = inside_context->s.last_poly->vertices;
  2283. sides = inside_context->s.last_poly->sides;
  2284. }
  2285. /* scale */
  2286. P.x *= inside_context->s.scalex;
  2287. P.y *= inside_context->s.scaley;
  2288. /* inside bounding box? */
  2289. if (fabs(P.x) > inside_context->s.box_URx ||
  2290. fabs(P.y) > inside_context->s.box_URy)
  2291. return false;
  2292. /* ellipses */
  2293. if (sides <= 2)
  2294. return hypot(P.x / inside_context->s.box_URx,
  2295. P.y / inside_context->s.box_URy) < 1;
  2296. /* use fast test in case we are converging on a segment */
  2297. size_t i = inside_context->s.last % sides; // in case last left over from larger polygon
  2298. size_t i1 = (i + 1) % sides;
  2299. Q = vertex[i + inside_context->s.outp];
  2300. R = vertex[i1 + inside_context->s.outp];
  2301. if (!same_side(P, O, Q, R)) /* false if outside the segment's face */
  2302. return false;
  2303. /* else inside the segment face... */
  2304. if ((s = same_side(P, Q, R, O)) && same_side(P, R, O, Q)) /* true if between the segment's sides */
  2305. return true;
  2306. /* else maybe in another segment */
  2307. for (size_t j = 1; j < sides; j++) { // iterate over remaining segments
  2308. if (s) { /* clockwise */
  2309. i = i1;
  2310. i1 = (i + 1) % sides;
  2311. } else { /* counter clockwise */
  2312. i1 = i;
  2313. i = (i + sides - 1) % sides;
  2314. }
  2315. if (!same_side(P, O, vertex[i + inside_context->s.outp],
  2316. vertex[i1 + inside_context->s.outp])) { // false if outside any other segment’s face
  2317. inside_context->s.last = i;
  2318. return false;
  2319. }
  2320. }
  2321. /* inside all segments' faces */
  2322. inside_context->s.last = i; // in case next edge is to same side
  2323. return true;
  2324. }
  2325. static int poly_path(node_t * n, port * p, int side, boxf rv[], int *kptr)
  2326. {
  2327. (void)n;
  2328. (void)p;
  2329. (void)side;
  2330. (void)rv;
  2331. (void)kptr;
  2332. return 0;
  2333. }
  2334. static unsigned char invflip_side(unsigned char side, int rankdir) {
  2335. switch (rankdir) {
  2336. case RANKDIR_TB:
  2337. break;
  2338. case RANKDIR_BT:
  2339. switch (side) {
  2340. case TOP:
  2341. side = BOTTOM;
  2342. break;
  2343. case BOTTOM:
  2344. side = TOP;
  2345. break;
  2346. default:
  2347. break;
  2348. }
  2349. break;
  2350. case RANKDIR_LR:
  2351. switch (side) {
  2352. case TOP:
  2353. side = RIGHT;
  2354. break;
  2355. case BOTTOM:
  2356. side = LEFT;
  2357. break;
  2358. case LEFT:
  2359. side = TOP;
  2360. break;
  2361. case RIGHT:
  2362. side = BOTTOM;
  2363. break;
  2364. default:
  2365. break;
  2366. }
  2367. break;
  2368. case RANKDIR_RL:
  2369. switch (side) {
  2370. case TOP:
  2371. side = RIGHT;
  2372. break;
  2373. case BOTTOM:
  2374. side = LEFT;
  2375. break;
  2376. case LEFT:
  2377. side = BOTTOM;
  2378. break;
  2379. case RIGHT:
  2380. side = TOP;
  2381. break;
  2382. default:
  2383. break;
  2384. }
  2385. break;
  2386. default:
  2387. UNREACHABLE();
  2388. }
  2389. return side;
  2390. }
  2391. static double invflip_angle(double angle, int rankdir)
  2392. {
  2393. switch (rankdir) {
  2394. case RANKDIR_TB:
  2395. break;
  2396. case RANKDIR_BT:
  2397. angle *= -1;
  2398. break;
  2399. case RANKDIR_LR:
  2400. angle -= M_PI * 0.5;
  2401. break;
  2402. case RANKDIR_RL:
  2403. if (angle == M_PI)
  2404. angle = -0.5 * M_PI;
  2405. else if (angle == M_PI * 0.75)
  2406. angle = -0.25 * M_PI;
  2407. else if (angle == M_PI * 0.5)
  2408. angle = 0;
  2409. else if (angle == 0)
  2410. angle = M_PI * 0.5;
  2411. else if (angle == M_PI * -0.25)
  2412. angle = M_PI * 0.75;
  2413. else if (angle == M_PI * -0.5)
  2414. angle = M_PI;
  2415. break;
  2416. default:
  2417. UNREACHABLE();
  2418. }
  2419. return angle;
  2420. }
  2421. /* compassPoint:
  2422. * Compute compass points for non-trivial shapes.
  2423. * It finds where the ray ((0,0),(x,y)) hits the boundary and
  2424. * returns it.
  2425. * Assumes ictxt and ictxt->n are non-NULL.
  2426. *
  2427. * bezier_clip uses the shape's _inside function, which assumes the input
  2428. * point is in the rotated coordinate system (as determined by rankdir), so
  2429. * it rotates the point counterclockwise based on rankdir to get the node's
  2430. * coordinate system.
  2431. * To handle this, if rankdir is set, we rotate (x,y) clockwise, and then
  2432. * rotate the answer counterclockwise.
  2433. */
  2434. static pointf compassPoint(inside_t * ictxt, double y, double x)
  2435. {
  2436. pointf curve[4]; /* bezier control points for a straight line */
  2437. node_t *n = ictxt->s.n;
  2438. graph_t* g = agraphof(n);
  2439. int rd = GD_rankdir(g);
  2440. pointf p;
  2441. p.x = x;
  2442. p.y = y;
  2443. if (rd)
  2444. p = cwrotatepf(p, 90 * rd);
  2445. curve[0].x = curve[0].y = 0;
  2446. curve[1] = curve[0];
  2447. curve[3] = curve[2] = p;
  2448. bezier_clip(ictxt, ND_shape(n)->fns->insidefn, curve, true);
  2449. if (rd)
  2450. curve[0] = ccwrotatepf(curve[0], 90 * rd);
  2451. return curve[0];
  2452. }
  2453. /* compassPort:
  2454. * Attach a compass point to a port pp, and fill in remaining fields.
  2455. * n is the corresponding node; bp is the bounding box of the port.
  2456. * compass is the compass point
  2457. * Return 1 if unrecognized compass point, in which case we
  2458. * use the center.
  2459. *
  2460. * This function also finishes initializing the port structure,
  2461. * even if no compass point is involved.
  2462. * The sides value gives the set of sides shared by the port. This
  2463. * is used with a compass point to indicate if the port is exposed, to
  2464. * set the port's side value.
  2465. *
  2466. * If ictxt is NULL, we are working with a simple rectangular shape (node or
  2467. * port of record of HTML label), so compass points are trivial. If ictxt is
  2468. * not NULL, it provides shape information so that the compass point can be
  2469. * calculated based on the shape.
  2470. *
  2471. * The code assumes the node has its unrotated shape to find the points,
  2472. * angles, etc. At the end, the parameters are adjusted to take into account
  2473. * the rankdir attribute. In particular, the first if-else statement flips
  2474. * the already adjusted ND_ht, ND_lw and ND_rw back to non-flipped values.
  2475. *
  2476. */
  2477. static int compassPort(node_t *n, boxf *bp, port *pp, const char *compass,
  2478. unsigned char sides, inside_t * ictxt) {
  2479. boxf b;
  2480. pointf p, ctr;
  2481. int rv = 0;
  2482. double theta = 0.0;
  2483. bool constrain = false;
  2484. bool dyna = false;
  2485. unsigned char side = 0;
  2486. bool clip = true;
  2487. bool defined;
  2488. double maxv; /* sufficiently large value outside of range of node */
  2489. if (bp) {
  2490. b = *bp;
  2491. p = (pointf){(b.LL.x + b.UR.x) / 2, (b.LL.y + b.UR.y) / 2};
  2492. defined = true;
  2493. } else {
  2494. p.x = p.y = 0.;
  2495. if (GD_flip(agraphof(n))) {
  2496. b.UR.x = ND_ht(n) / 2.;
  2497. b.LL.x = -b.UR.x;
  2498. b.UR.y = ND_lw(n);
  2499. b.LL.y = -b.UR.y;
  2500. } else {
  2501. b.UR.y = ND_ht(n) / 2.;
  2502. b.LL.y = -b.UR.y;
  2503. b.UR.x = ND_lw(n);
  2504. b.LL.x = -b.UR.x;
  2505. }
  2506. defined = false;
  2507. }
  2508. maxv = fmax(b.UR.x, b.UR.y);
  2509. maxv *= 4.0;
  2510. ctr = p;
  2511. if (compass && *compass) {
  2512. switch (*compass++) {
  2513. case 'e':
  2514. if (*compass)
  2515. rv = 1;
  2516. else {
  2517. if (ictxt)
  2518. p = compassPoint(ictxt, ctr.y, maxv);
  2519. else
  2520. p.x = b.UR.x;
  2521. theta = 0.0;
  2522. constrain = true;
  2523. defined = true;
  2524. clip = false;
  2525. side = sides & RIGHT;
  2526. }
  2527. break;
  2528. case 's':
  2529. p.y = b.LL.y;
  2530. constrain = true;
  2531. clip = false;
  2532. switch (*compass) {
  2533. case '\0':
  2534. theta = -M_PI * 0.5;
  2535. defined = true;
  2536. if (ictxt)
  2537. p = compassPoint(ictxt, -maxv, ctr.x);
  2538. else
  2539. p.x = ctr.x;
  2540. side = sides & BOTTOM;
  2541. break;
  2542. case 'e':
  2543. theta = -M_PI * 0.25;
  2544. defined = true;
  2545. if (ictxt)
  2546. p = compassPoint(ictxt, -maxv, maxv);
  2547. else
  2548. p.x = b.UR.x;
  2549. side = sides & (BOTTOM | RIGHT);
  2550. break;
  2551. case 'w':
  2552. theta = -M_PI * 0.75;
  2553. defined = true;
  2554. if (ictxt)
  2555. p = compassPoint(ictxt, -maxv, -maxv);
  2556. else
  2557. p.x = b.LL.x;
  2558. side = sides & (BOTTOM | LEFT);
  2559. break;
  2560. default:
  2561. p.y = ctr.y;
  2562. constrain = false;
  2563. clip = true;
  2564. rv = 1;
  2565. break;
  2566. }
  2567. break;
  2568. case 'w':
  2569. if (*compass)
  2570. rv = 1;
  2571. else {
  2572. if (ictxt)
  2573. p = compassPoint(ictxt, ctr.y, -maxv);
  2574. else
  2575. p.x = b.LL.x;
  2576. theta = M_PI;
  2577. constrain = true;
  2578. defined = true;
  2579. clip = false;
  2580. side = sides & LEFT;
  2581. }
  2582. break;
  2583. case 'n':
  2584. p.y = b.UR.y;
  2585. constrain = true;
  2586. clip = false;
  2587. switch (*compass) {
  2588. case '\0':
  2589. defined = true;
  2590. theta = M_PI * 0.5;
  2591. if (ictxt)
  2592. p = compassPoint(ictxt, maxv, ctr.x);
  2593. else
  2594. p.x = ctr.x;
  2595. side = sides & TOP;
  2596. break;
  2597. case 'e':
  2598. defined = true;
  2599. theta = M_PI * 0.25;
  2600. if (ictxt)
  2601. p = compassPoint(ictxt, maxv, maxv);
  2602. else
  2603. p.x = b.UR.x;
  2604. side = sides & (TOP | RIGHT);
  2605. break;
  2606. case 'w':
  2607. defined = true;
  2608. theta = M_PI * 0.75;
  2609. if (ictxt)
  2610. p = compassPoint(ictxt, maxv, -maxv);
  2611. else
  2612. p.x = b.LL.x;
  2613. side = sides & (TOP | LEFT);
  2614. break;
  2615. default:
  2616. p.y = ctr.y;
  2617. constrain = false;
  2618. clip = true;
  2619. rv = 1;
  2620. break;
  2621. }
  2622. break;
  2623. case '_':
  2624. dyna = true;
  2625. side = sides;
  2626. break;
  2627. case 'c':
  2628. break;
  2629. default:
  2630. rv = 1;
  2631. break;
  2632. }
  2633. }
  2634. p = cwrotatepf(p, 90 * GD_rankdir(agraphof(n)));
  2635. if (dyna)
  2636. pp->side = side;
  2637. else
  2638. pp->side = invflip_side(side, GD_rankdir(agraphof(n)));
  2639. pp->bp = bp;
  2640. pp->p = p;
  2641. pp->theta = invflip_angle(theta, GD_rankdir(agraphof(n)));
  2642. if (p.x == 0 && p.y == 0)
  2643. pp->order = MC_SCALE / 2;
  2644. else {
  2645. /* compute angle with 0 at north pole, increasing CCW */
  2646. double angle = atan2(p.y, p.x) + 1.5 * M_PI;
  2647. if (angle >= 2 * M_PI)
  2648. angle -= 2 * M_PI;
  2649. pp->order = (int) (MC_SCALE * angle / (2 * M_PI));
  2650. }
  2651. pp->constrained = constrain;
  2652. pp->defined = defined;
  2653. pp->clip = clip;
  2654. pp->dyna = dyna;
  2655. return rv;
  2656. }
  2657. static port poly_port(node_t * n, char *portname, char *compass)
  2658. {
  2659. port rv;
  2660. boxf *bp;
  2661. unsigned char sides; // bitmap of which sides the port lies along
  2662. if (portname[0] == '\0')
  2663. return Center;
  2664. if (compass == NULL)
  2665. compass = "_";
  2666. sides = BOTTOM | RIGHT | TOP | LEFT;
  2667. if (ND_label(n)->html && (bp = html_port(n, portname, &sides))) {
  2668. if (compassPort(n, bp, &rv, compass, sides, NULL)) {
  2669. agwarningf(
  2670. "node %s, port %s, unrecognized compass point '%s' - ignored\n",
  2671. agnameof(n), portname, compass);
  2672. }
  2673. } else {
  2674. inside_t *ictxtp;
  2675. inside_t ictxt = {0};
  2676. if (IS_BOX(n))
  2677. ictxtp = NULL;
  2678. else {
  2679. ictxt.s.n = n;
  2680. ictxt.s.bp = NULL;
  2681. ictxtp = &ictxt;
  2682. }
  2683. if (compassPort(n, NULL, &rv, portname, sides, ictxtp))
  2684. unrecognized(n, portname);
  2685. }
  2686. rv.name = NULL;
  2687. return rv;
  2688. }
  2689. static bool multicolor(const char *f) {
  2690. return strchr(f, ':') != NULL;
  2691. }
  2692. /* generic polygon gencode routine */
  2693. static void poly_gencode(GVJ_t * job, node_t * n)
  2694. {
  2695. obj_state_t *obj = job->obj;
  2696. polygon_t *poly;
  2697. double xsize, ysize;
  2698. pointf P, *vertices;
  2699. int filled;
  2700. bool usershape_p;
  2701. bool pfilled; /* true if fill not handled by user shape */
  2702. char *color, *name;
  2703. int doMap = (obj->url || obj->explicit_tooltip);
  2704. char* fillcolor=NULL;
  2705. char* pencolor=NULL;
  2706. if (doMap && !(job->flags & EMIT_CLUSTERS_LAST))
  2707. gvrender_begin_anchor(job,
  2708. obj->url, obj->tooltip, obj->target,
  2709. obj->id);
  2710. poly = ND_shape_info(n);
  2711. vertices = poly->vertices;
  2712. const size_t sides = poly->sides;
  2713. size_t peripheries = poly->peripheries;
  2714. pointf *AF = gv_calloc(sides + 5, sizeof(pointf));
  2715. /* nominal label position in the center of the node */
  2716. ND_label(n)->pos = ND_coord(n);
  2717. xsize = (ND_lw(n) + ND_rw(n)) / INCH2PS(ND_width(n));
  2718. ysize = ND_ht(n) / INCH2PS(ND_height(n));
  2719. const graphviz_polygon_style_t style = stylenode(job, n);
  2720. char *clrs[2] = {0};
  2721. if (ND_gui_state(n) & GUI_STATE_ACTIVE) {
  2722. pencolor = DEFAULT_ACTIVEPENCOLOR;
  2723. gvrender_set_pencolor(job, pencolor);
  2724. color = DEFAULT_ACTIVEFILLCOLOR;
  2725. gvrender_set_fillcolor(job, color);
  2726. filled = FILL;
  2727. } else if (ND_gui_state(n) & GUI_STATE_SELECTED) {
  2728. pencolor = DEFAULT_SELECTEDPENCOLOR;
  2729. gvrender_set_pencolor(job, pencolor);
  2730. color = DEFAULT_SELECTEDFILLCOLOR;
  2731. gvrender_set_fillcolor(job, color);
  2732. filled = FILL;
  2733. } else if (ND_gui_state(n) & GUI_STATE_DELETED) {
  2734. pencolor = DEFAULT_DELETEDPENCOLOR;
  2735. gvrender_set_pencolor(job, pencolor);
  2736. color = DEFAULT_DELETEDFILLCOLOR;
  2737. gvrender_set_fillcolor(job, color);
  2738. filled = FILL;
  2739. } else if (ND_gui_state(n) & GUI_STATE_VISITED) {
  2740. pencolor = DEFAULT_VISITEDPENCOLOR;
  2741. gvrender_set_pencolor(job, pencolor);
  2742. color = DEFAULT_VISITEDFILLCOLOR;
  2743. gvrender_set_fillcolor(job, color);
  2744. filled = FILL;
  2745. } else {
  2746. if (style.filled) {
  2747. double frac;
  2748. fillcolor = findFill (n);
  2749. if (findStopColor (fillcolor, clrs, &frac)) {
  2750. gvrender_set_fillcolor(job, clrs[0]);
  2751. if (clrs[1])
  2752. gvrender_set_gradient_vals(job,clrs[1],late_int(n,N_gradientangle,0,0), frac);
  2753. else
  2754. gvrender_set_gradient_vals(job,DEFAULT_COLOR,late_int(n,N_gradientangle,0,0), frac);
  2755. if (style.radial)
  2756. filled = RGRADIENT;
  2757. else
  2758. filled = GRADIENT;
  2759. }
  2760. else {
  2761. gvrender_set_fillcolor(job, fillcolor);
  2762. filled = FILL;
  2763. }
  2764. }
  2765. else if (style.striped || style.wedged) {
  2766. fillcolor = findFill (n);
  2767. filled = 1;
  2768. }
  2769. else {
  2770. filled = 0;
  2771. }
  2772. pencolor = penColor(job, n); /* emit pen color */
  2773. }
  2774. pfilled = !ND_shape(n)->usershape || streq(ND_shape(n)->name, "custom");
  2775. /* if no boundary but filled, set boundary color to transparent */
  2776. if (peripheries == 0 && filled != 0 && pfilled) {
  2777. peripheries = 1;
  2778. gvrender_set_pencolor(job, "transparent");
  2779. }
  2780. /* draw peripheries first */
  2781. size_t j;
  2782. for (j = 0; j < peripheries; j++) {
  2783. for (size_t i = 0; i < sides; i++) {
  2784. P = vertices[i + j * sides];
  2785. AF[i].x = P.x * xsize + ND_coord(n).x;
  2786. AF[i].y = P.y * ysize + ND_coord(n).y;
  2787. }
  2788. if (sides <= 2) {
  2789. if (style.wedged && j == 0 && multicolor(fillcolor)) {
  2790. int rv = wedgedEllipse (job, AF, fillcolor);
  2791. if (rv > 1)
  2792. agerr (AGPREV, "in node %s\n", agnameof(n));
  2793. filled = 0;
  2794. }
  2795. gvrender_ellipse(job, AF, filled);
  2796. if (style.diagonals) {
  2797. Mcircle_hack(job, n);
  2798. }
  2799. } else if (style.striped) {
  2800. if (j == 0) {
  2801. int rv = stripedBox (job, AF, fillcolor, 1);
  2802. if (rv > 1)
  2803. agerr (AGPREV, "in node %s\n", agnameof(n));
  2804. }
  2805. gvrender_polygon(job, AF, sides, 0);
  2806. } else if (style.underline) {
  2807. gvrender_set_pencolor(job, "transparent");
  2808. gvrender_polygon(job, AF, sides, filled);
  2809. gvrender_set_pencolor(job, pencolor);
  2810. gvrender_polyline(job, AF+2, 2);
  2811. } else if (SPECIAL_CORNERS(style)) {
  2812. round_corners(job, AF, sides, style, filled);
  2813. } else {
  2814. gvrender_polygon(job, AF, sides, filled);
  2815. }
  2816. /* fill innermost periphery only */
  2817. filled = 0;
  2818. }
  2819. usershape_p = false;
  2820. if (ND_shape(n)->usershape) {
  2821. name = ND_shape(n)->name;
  2822. if (streq(name, "custom")) {
  2823. if ((name = agget(n, "shapefile")) && name[0])
  2824. usershape_p = true;
  2825. } else
  2826. usershape_p = true;
  2827. } else if ((name = agget(n, "image")) && name[0]) {
  2828. usershape_p = true;
  2829. }
  2830. if (usershape_p) {
  2831. /* get coords of innermost periphery */
  2832. for (size_t i = 0; i < sides; i++) {
  2833. P = vertices[i];
  2834. AF[i].x = P.x * xsize + ND_coord(n).x;
  2835. AF[i].y = P.y * ysize + ND_coord(n).y;
  2836. }
  2837. /* lay down fill first */
  2838. if (filled != 0 && pfilled) {
  2839. if (sides <= 2) {
  2840. if (style.wedged && j == 0 && multicolor(fillcolor)) {
  2841. int rv = wedgedEllipse (job, AF, fillcolor);
  2842. if (rv > 1)
  2843. agerr (AGPREV, "in node %s\n", agnameof(n));
  2844. filled = 0;
  2845. }
  2846. gvrender_ellipse(job, AF, filled);
  2847. if (style.diagonals) {
  2848. Mcircle_hack(job, n);
  2849. }
  2850. } else if (style.striped) {
  2851. int rv = stripedBox (job, AF, fillcolor, 1);
  2852. if (rv > 1)
  2853. agerr (AGPREV, "in node %s\n", agnameof(n));
  2854. gvrender_polygon(job, AF, sides, 0);
  2855. } else if (style.rounded || style.diagonals) {
  2856. round_corners(job, AF, sides, style, filled);
  2857. } else {
  2858. gvrender_polygon(job, AF, sides, filled);
  2859. }
  2860. }
  2861. gvrender_usershape(job, name, AF, sides, filled != 0,
  2862. late_string(n, N_imagescale, "false"),
  2863. late_string(n, N_imagepos, "mc"));
  2864. filled = 0; /* with user shapes, we have done the fill if needed */
  2865. }
  2866. free(AF);
  2867. free (clrs[0]);
  2868. free (clrs[1]);
  2869. emit_label(job, EMIT_NLABEL, ND_label(n));
  2870. if (doMap) {
  2871. if (job->flags & EMIT_CLUSTERS_LAST)
  2872. gvrender_begin_anchor(job,
  2873. obj->url, obj->tooltip, obj->target,
  2874. obj->id);
  2875. gvrender_end_anchor(job);
  2876. }
  2877. }
  2878. /*=======================end poly======================================*/
  2879. /*===============================point start========================*/
  2880. /* point_init:
  2881. * shorthand for shape=circle, style=filled, width=0.05, label=""
  2882. */
  2883. static void point_init(node_t * n)
  2884. {
  2885. polygon_t *poly = gv_alloc(sizeof(polygon_t));
  2886. size_t sides, outp, peripheries = ND_shape(n)->polygon->peripheries;
  2887. double sz;
  2888. pointf P, *vertices;
  2889. size_t i, j;
  2890. double w, h;
  2891. /* set width and height, and make them equal
  2892. * if user has set weight or height, use it.
  2893. * if both are set, use smallest.
  2894. * if neither, use default
  2895. */
  2896. w = late_double(n, N_width, DBL_MAX, MIN_NODEWIDTH);
  2897. h = late_double(n, N_height, DBL_MAX, MIN_NODEHEIGHT);
  2898. w = fmin(w, h);
  2899. if (is_exactly_equal(w, DBL_MAX) &&
  2900. is_exactly_equal(h, DBL_MAX)) // neither defined
  2901. ND_width(n) = ND_height(n) = DEF_POINT;
  2902. else {
  2903. w = fmin(w, h);
  2904. /* If w == 0, use it; otherwise, make w no less than MIN_POINT due
  2905. * to the restrictions mentioned above.
  2906. */
  2907. if (w > 0.0)
  2908. w = fmax(w, MIN_POINT);
  2909. ND_width(n) = ND_height(n) = w;
  2910. }
  2911. sz = ND_width(n) * POINTS_PER_INCH;
  2912. peripheries = (size_t)late_int(n, N_peripheries, (int)peripheries, 0);
  2913. if (peripheries < 1)
  2914. outp = 1;
  2915. else
  2916. outp = peripheries;
  2917. sides = 2;
  2918. const double penwidth = late_int(n, N_penwidth, DEFAULT_NODEPENWIDTH, MIN_NODEPENWIDTH);
  2919. if (peripheries >= 1 && penwidth > 0) {
  2920. // allocate extra vertices representing the outline, i.e., the outermost
  2921. // periphery with penwidth taken into account
  2922. ++outp;
  2923. }
  2924. vertices = gv_calloc(outp * sides, sizeof(pointf));
  2925. P.y = P.x = sz / 2.;
  2926. vertices[0].x = -P.x;
  2927. vertices[0].y = -P.y;
  2928. vertices[1] = P;
  2929. if (peripheries > 1) {
  2930. for (j = 1, i = 2; j < peripheries; j++) {
  2931. P.x += GAP;
  2932. P.y += GAP;
  2933. vertices[i].x = -P.x;
  2934. vertices[i].y = -P.y;
  2935. i++;
  2936. vertices[i].x = P.x;
  2937. vertices[i].y = P.y;
  2938. i++;
  2939. }
  2940. sz = 2. * P.x;
  2941. } else {
  2942. i = sides;
  2943. }
  2944. if (peripheries >= 1 && penwidth > 0 && outp > peripheries) {
  2945. // add an outline at half the penwidth outside the outermost periphery
  2946. P.x += penwidth / 2;
  2947. P.y += penwidth / 2;
  2948. vertices[i].x = -P.x;
  2949. vertices[i].y = -P.y;
  2950. i++;
  2951. vertices[i].x = P.x;
  2952. vertices[i].y = P.y;
  2953. i++;
  2954. }
  2955. const double sz_outline = 2. * P.x;
  2956. poly->regular = true;
  2957. poly->peripheries = peripheries;
  2958. poly->sides = 2;
  2959. poly->orientation = 0;
  2960. poly->skew = 0;
  2961. poly->distortion = 0;
  2962. poly->vertices = vertices;
  2963. ND_height(n) = ND_width(n) = PS2INCH(sz);
  2964. ND_outline_height(n) = ND_outline_width(n) = PS2INCH(sz_outline);
  2965. ND_shape_info(n) = poly;
  2966. }
  2967. static bool point_inside(inside_t * inside_context, pointf p)
  2968. {
  2969. pointf P;
  2970. node_t *n;
  2971. if (!inside_context) {
  2972. return false;
  2973. }
  2974. n = inside_context->s.n;
  2975. P = ccwrotatepf(p, 90 * GD_rankdir(agraphof(n)));
  2976. if (n != inside_context->s.lastn) {
  2977. size_t outp;
  2978. polygon_t *poly = ND_shape_info(n);
  2979. const size_t sides = 2;
  2980. const double penwidth = late_int(n, N_penwidth, DEFAULT_NODEPENWIDTH, MIN_NODEPENWIDTH);
  2981. if (poly->peripheries >= 1 && penwidth > 0) {
  2982. /* index to outline, i.e., the outer-periphery with penwidth taken into account */
  2983. outp = sides * (poly->peripheries + 1 - 1);
  2984. } else if (poly->peripheries < 1) {
  2985. outp = 0;
  2986. } else {
  2987. /* index to outer-periphery */
  2988. outp = sides * (poly->peripheries - 1);
  2989. }
  2990. inside_context->s.radius = poly->vertices[outp + 1].x;
  2991. inside_context->s.lastn = n;
  2992. }
  2993. /* inside bounding box? */
  2994. if (fabs(P.x) > inside_context->s.radius ||
  2995. fabs(P.y) > inside_context->s.radius)
  2996. return false;
  2997. return hypot(P.x, P.y) <= inside_context->s.radius;
  2998. }
  2999. static void point_gencode(GVJ_t * job, node_t * n)
  3000. {
  3001. obj_state_t *obj = job->obj;
  3002. polygon_t *poly;
  3003. pointf P, *vertices;
  3004. bool filled;
  3005. char *color;
  3006. int doMap = obj->url || obj->explicit_tooltip;
  3007. if (doMap && !(job->flags & EMIT_CLUSTERS_LAST))
  3008. gvrender_begin_anchor(job,
  3009. obj->url, obj->tooltip, obj->target,
  3010. obj->id);
  3011. poly = ND_shape_info(n);
  3012. vertices = poly->vertices;
  3013. const size_t sides = poly->sides;
  3014. size_t peripheries = poly->peripheries;
  3015. graphviz_polygon_style_t style = {0};
  3016. checkStyle(n, &style);
  3017. if (style.invisible)
  3018. gvrender_set_style(job, point_style);
  3019. else
  3020. gvrender_set_style(job, &point_style[1]);
  3021. if (N_penwidth)
  3022. gvrender_set_penwidth(job, late_double(n, N_penwidth, 1.0, 0.0));
  3023. if (ND_gui_state(n) & GUI_STATE_ACTIVE) {
  3024. color = DEFAULT_ACTIVEPENCOLOR;
  3025. gvrender_set_pencolor(job, color);
  3026. color = DEFAULT_ACTIVEFILLCOLOR;
  3027. gvrender_set_fillcolor(job, color);
  3028. } else if (ND_gui_state(n) & GUI_STATE_SELECTED) {
  3029. color = DEFAULT_SELECTEDPENCOLOR;
  3030. gvrender_set_pencolor(job, color);
  3031. color = DEFAULT_SELECTEDFILLCOLOR;
  3032. gvrender_set_fillcolor(job, color);
  3033. } else if (ND_gui_state(n) & GUI_STATE_DELETED) {
  3034. color = DEFAULT_DELETEDPENCOLOR;
  3035. gvrender_set_pencolor(job, color);
  3036. color = DEFAULT_DELETEDFILLCOLOR;
  3037. gvrender_set_fillcolor(job, color);
  3038. } else if (ND_gui_state(n) & GUI_STATE_VISITED) {
  3039. color = DEFAULT_VISITEDPENCOLOR;
  3040. gvrender_set_pencolor(job, color);
  3041. color = DEFAULT_VISITEDFILLCOLOR;
  3042. gvrender_set_fillcolor(job, color);
  3043. } else {
  3044. color = findFillDflt(n, "black");
  3045. gvrender_set_fillcolor(job, color); /* emit fill color */
  3046. penColor(job, n); /* emit pen color */
  3047. }
  3048. filled = true;
  3049. /* if no boundary but filled, set boundary color to fill color */
  3050. if (peripheries == 0) {
  3051. peripheries = 1;
  3052. if (color[0])
  3053. gvrender_set_pencolor(job, color);
  3054. }
  3055. for (size_t j = 0; j < peripheries; j++) {
  3056. enum {A_size = 2};
  3057. pointf AF[A_size] = {{0}};
  3058. for (size_t i = 0; i < sides; i++) {
  3059. P = vertices[i + j * sides];
  3060. if (i < A_size) {
  3061. AF[i].x = P.x + ND_coord(n).x;
  3062. AF[i].y = P.y + ND_coord(n).y;
  3063. }
  3064. }
  3065. gvrender_ellipse(job, AF, filled);
  3066. /* fill innermost periphery only */
  3067. filled = false;
  3068. }
  3069. if (doMap) {
  3070. if (job->flags & EMIT_CLUSTERS_LAST)
  3071. gvrender_begin_anchor(job,
  3072. obj->url, obj->tooltip, obj->target,
  3073. obj->id);
  3074. gvrender_end_anchor(job);
  3075. }
  3076. }
  3077. /* the "record" shape is a rudimentary table formatter */
  3078. #define HASTEXT 1
  3079. #define HASPORT 2
  3080. #define HASTABLE 4
  3081. #define INTEXT 8
  3082. #define INPORT 16
  3083. static bool ISCTRL(int c) {
  3084. return c == '{' || c == '}' || c == '|' || c == '<' || c == '>';
  3085. }
  3086. static char *reclblp;
  3087. static void free_field(field_t * f)
  3088. {
  3089. int i;
  3090. for (i = 0; i < f->n_flds; i++) {
  3091. free_field(f->fld[i]);
  3092. }
  3093. free(f->id);
  3094. free_label(f->lp);
  3095. free(f->fld);
  3096. free(f);
  3097. }
  3098. /* parse_error:
  3099. * Clean up memory allocated in parse_reclbl, then return NULL
  3100. */
  3101. static field_t *parse_error(field_t *rv, char *portname) {
  3102. free_field(rv);
  3103. free(portname);
  3104. return NULL;
  3105. }
  3106. static field_t *parse_reclbl(node_t *n, bool LR, bool flag, char *text) {
  3107. field_t *fp, *rv = gv_alloc(sizeof(field_t));
  3108. char *tsp, *psp=NULL, *hstsp, *hspsp=NULL, *sp;
  3109. char *tmpport = NULL;
  3110. int cnt, mode, fi;
  3111. textlabel_t *lbl = ND_label(n);
  3112. unsigned char uc;
  3113. fp = NULL;
  3114. size_t maxf;
  3115. for (maxf = 1, cnt = 0, sp = reclblp; *sp; sp++) {
  3116. if (*sp == '\\') {
  3117. sp++;
  3118. if (*sp && (*sp == '{' || *sp == '}' || *sp == '|' || *sp == '\\'))
  3119. continue;
  3120. }
  3121. if (*sp == '{')
  3122. cnt++;
  3123. else if (*sp == '}')
  3124. cnt--;
  3125. else if (*sp == '|' && cnt == 0)
  3126. maxf++;
  3127. if (cnt < 0)
  3128. break;
  3129. }
  3130. rv->fld = gv_calloc(maxf, sizeof(field_t*));
  3131. rv->LR = LR;
  3132. mode = 0;
  3133. fi = 0;
  3134. hstsp = tsp = text;
  3135. bool wflag = true;
  3136. bool ishardspace = false;
  3137. while (wflag) {
  3138. if ((uc = *(unsigned char*)reclblp) && uc < ' ') { /* Ignore non-0 control characters */
  3139. reclblp++;
  3140. continue;
  3141. }
  3142. switch (*reclblp) {
  3143. case '<':
  3144. if (mode & (HASTABLE | HASPORT))
  3145. return parse_error(rv, tmpport);
  3146. if (lbl->html)
  3147. goto dotext;
  3148. mode |= (HASPORT | INPORT);
  3149. reclblp++;
  3150. hspsp = psp = text;
  3151. break;
  3152. case '>':
  3153. if (lbl->html)
  3154. goto dotext;
  3155. if (!(mode & INPORT))
  3156. return parse_error(rv, tmpport);
  3157. if (psp > text + 1 && psp - 1 != hspsp && *(psp - 1) == ' ')
  3158. psp--;
  3159. *psp = '\000';
  3160. tmpport = gv_strdup(text);
  3161. mode &= ~INPORT;
  3162. reclblp++;
  3163. break;
  3164. case '{':
  3165. reclblp++;
  3166. if (mode != 0 || !*reclblp)
  3167. return parse_error(rv, tmpport);
  3168. mode = HASTABLE;
  3169. if (!(rv->fld[fi++] = parse_reclbl(n, !LR, false, text)))
  3170. return parse_error(rv, tmpport);
  3171. break;
  3172. case '}':
  3173. case '|':
  3174. case '\000':
  3175. if ((!*reclblp && !flag) || (mode & INPORT))
  3176. return parse_error(rv, tmpport);
  3177. if (!(mode & HASTABLE))
  3178. fp = rv->fld[fi++] = gv_alloc(sizeof(field_t));
  3179. if (tmpport) {
  3180. fp->id = tmpport;
  3181. tmpport = NULL;
  3182. }
  3183. if (!(mode & (HASTEXT | HASTABLE))) {
  3184. mode |= HASTEXT;
  3185. *tsp++ = ' ';
  3186. }
  3187. if (mode & HASTEXT) {
  3188. if (tsp > text + 1 && tsp - 1 != hstsp && *(tsp - 1) == ' ')
  3189. tsp--;
  3190. *tsp = '\000';
  3191. fp->lp =
  3192. make_label(n, text,
  3193. lbl->html ? LT_HTML : LT_NONE,
  3194. lbl->fontsize, lbl->fontname, lbl->fontcolor);
  3195. fp->LR = true;
  3196. hstsp = tsp = text;
  3197. }
  3198. if (*reclblp) {
  3199. if (*reclblp == '}') {
  3200. reclblp++;
  3201. rv->n_flds = fi;
  3202. return rv;
  3203. }
  3204. mode = 0;
  3205. reclblp++;
  3206. } else
  3207. wflag = false;
  3208. break;
  3209. case '\\':
  3210. if (*(reclblp + 1)) {
  3211. if (ISCTRL(*(reclblp + 1))) {
  3212. // nothing
  3213. } else if (*(reclblp + 1) == ' ' && !lbl->html)
  3214. ishardspace = true;
  3215. else {
  3216. *tsp++ = '\\';
  3217. mode |= INTEXT | HASTEXT;
  3218. }
  3219. reclblp++;
  3220. }
  3221. /* fall through */
  3222. default:
  3223. dotext:
  3224. if ((mode & HASTABLE) && *reclblp != ' ')
  3225. return parse_error(rv, tmpport);
  3226. if (!(mode & (INTEXT | INPORT)) && *reclblp != ' ')
  3227. mode |= INTEXT | HASTEXT;
  3228. if (mode & INTEXT) {
  3229. if (!(*reclblp == ' ' && !ishardspace && *(tsp - 1) == ' '
  3230. && !lbl->html))
  3231. *tsp++ = *reclblp;
  3232. if (ishardspace)
  3233. hstsp = tsp - 1;
  3234. } else if (mode & INPORT) {
  3235. if (!(*reclblp == ' ' && !ishardspace &&
  3236. (psp == text || *(psp - 1) == ' ')))
  3237. *psp++ = *reclblp;
  3238. if (ishardspace)
  3239. hspsp = psp - 1;
  3240. }
  3241. reclblp++;
  3242. while ((*reclblp & 0xc0) == 0x80)
  3243. *tsp++ = *reclblp++;
  3244. break;
  3245. }
  3246. }
  3247. rv->n_flds = fi;
  3248. return rv;
  3249. }
  3250. static pointf size_reclbl(node_t * n, field_t * f)
  3251. {
  3252. int i;
  3253. char *p;
  3254. double marginx, marginy;
  3255. pointf d, d0;
  3256. pointf dimen;
  3257. if (f->lp) {
  3258. dimen = f->lp->dimen;
  3259. /* minimal whitespace around label */
  3260. if (dimen.x > 0.0 || dimen.y > 0.0) {
  3261. /* padding */
  3262. if ((p = agget(n, "margin"))) {
  3263. i = sscanf(p, "%lf,%lf", &marginx, &marginy);
  3264. if (i > 0) {
  3265. dimen.x += 2 * INCH2PS(marginx);
  3266. if (i > 1)
  3267. dimen.y += 2 * INCH2PS(marginy);
  3268. else
  3269. dimen.y += 2 * INCH2PS(marginx);
  3270. } else
  3271. PAD(dimen);
  3272. } else
  3273. PAD(dimen);
  3274. }
  3275. d = dimen;
  3276. } else {
  3277. d.x = d.y = 0;
  3278. for (i = 0; i < f->n_flds; i++) {
  3279. d0 = size_reclbl(n, f->fld[i]);
  3280. if (f->LR) {
  3281. d.x += d0.x;
  3282. d.y = fmax(d.y, d0.y);
  3283. } else {
  3284. d.y += d0.y;
  3285. d.x = fmax(d.x, d0.x);
  3286. }
  3287. }
  3288. }
  3289. f->size = d;
  3290. return d;
  3291. }
  3292. static void resize_reclbl(field_t *f, pointf sz, bool nojustify_p) {
  3293. int i, amt;
  3294. double inc;
  3295. pointf d;
  3296. pointf newsz;
  3297. field_t *sf;
  3298. /* adjust field */
  3299. d.x = sz.x - f->size.x;
  3300. d.y = sz.y - f->size.y;
  3301. f->size = sz;
  3302. /* adjust text area */
  3303. if (f->lp && !nojustify_p) {
  3304. f->lp->space.x += d.x;
  3305. f->lp->space.y += d.y;
  3306. }
  3307. /* adjust children */
  3308. if (f->n_flds) {
  3309. if (f->LR)
  3310. inc = d.x / f->n_flds;
  3311. else
  3312. inc = d.y / f->n_flds;
  3313. for (i = 0; i < f->n_flds; i++) {
  3314. sf = f->fld[i];
  3315. amt = (int)((i + 1) * inc) - (int)(i * inc);
  3316. if (f->LR)
  3317. newsz = (pointf){sf->size.x + amt, sz.y};
  3318. else
  3319. newsz = (pointf){sz.x, sf->size.y + amt};
  3320. resize_reclbl(sf, newsz, nojustify_p);
  3321. }
  3322. }
  3323. }
  3324. /* pos_reclbl:
  3325. * Assign position info for each field. Also, set
  3326. * the sides attribute, which indicates which sides of the
  3327. * record are accessible to the field.
  3328. */
  3329. static void pos_reclbl(field_t *f, pointf ul, unsigned char sides) {
  3330. int i, last;
  3331. unsigned char mask;
  3332. f->sides = sides;
  3333. f->b.LL = (pointf){ul.x, ul.y - f->size.y};
  3334. f->b.UR = (pointf){ul.x + f->size.x, ul.y};
  3335. last = f->n_flds - 1;
  3336. for (i = 0; i <= last; i++) {
  3337. if (sides) {
  3338. if (f->LR) {
  3339. if (i == 0) {
  3340. if (i == last)
  3341. mask = TOP | BOTTOM | RIGHT | LEFT;
  3342. else
  3343. mask = TOP | BOTTOM | LEFT;
  3344. } else if (i == last)
  3345. mask = TOP | BOTTOM | RIGHT;
  3346. else
  3347. mask = TOP | BOTTOM;
  3348. } else {
  3349. if (i == 0) {
  3350. if (i == last)
  3351. mask = TOP | BOTTOM | RIGHT | LEFT;
  3352. else
  3353. mask = TOP | RIGHT | LEFT;
  3354. } else if (i == last)
  3355. mask = LEFT | BOTTOM | RIGHT;
  3356. else
  3357. mask = LEFT | RIGHT;
  3358. }
  3359. } else
  3360. mask = 0;
  3361. pos_reclbl(f->fld[i], ul, (unsigned char)(sides & mask));
  3362. if (f->LR)
  3363. ul.x = ul.x + f->fld[i]->size.x;
  3364. else
  3365. ul.y = ul.y - f->fld[i]->size.y;
  3366. }
  3367. }
  3368. #if defined(DEBUG) && DEBUG > 1
  3369. static void indent(int l)
  3370. {
  3371. int i;
  3372. for (i = 0; i < l; i++)
  3373. fputs(" ", stderr);
  3374. }
  3375. static void prbox(boxf b)
  3376. {
  3377. fprintf(stderr, "((%.5g,%.5g),(%.5g,%.5g))\n", b.LL.x, b.LL.y, b.UR.x,
  3378. b.UR.y);
  3379. }
  3380. static void dumpL(field_t * info, int level)
  3381. {
  3382. int i;
  3383. indent(level);
  3384. if (info->n_flds == 0) {
  3385. fprintf(stderr, "Label \"%s\" ", info->lp->text);
  3386. prbox(info->b);
  3387. } else {
  3388. fprintf(stderr, "Tbl ");
  3389. prbox(info->b);
  3390. for (i = 0; i < info->n_flds; i++) {
  3391. dumpL(info->fld[i], level + 1);
  3392. }
  3393. }
  3394. }
  3395. #endif
  3396. /* syntax of labels: foo|bar|baz or foo|(recursive|label)|baz */
  3397. static void record_init(node_t * n)
  3398. {
  3399. field_t *info;
  3400. pointf sz;
  3401. int flip;
  3402. size_t len;
  3403. unsigned char sides = BOTTOM | RIGHT | TOP | LEFT;
  3404. /* Always use rankdir to determine how records are laid out */
  3405. flip = !GD_realflip(agraphof(n));
  3406. reclblp = ND_label(n)->text;
  3407. len = strlen(reclblp);
  3408. /* For some forgotten reason, an empty label is parsed into a space, so
  3409. * we need at least two bytes in textbuf, as well as accounting for the
  3410. * error path involving "\\N" below.
  3411. */
  3412. len = MAX(MAX(len, 1), strlen("\\N"));
  3413. char *textbuf = gv_calloc(len + 1, sizeof(char)); // temp buffer for storing labels
  3414. if (!(info = parse_reclbl(n, flip, true, textbuf))) {
  3415. agerrorf("bad label format %s\n", ND_label(n)->text);
  3416. reclblp = "\\N";
  3417. info = parse_reclbl(n, flip, true, textbuf);
  3418. }
  3419. free(textbuf);
  3420. size_reclbl(n, info);
  3421. sz.x = INCH2PS(ND_width(n));
  3422. sz.y = INCH2PS(ND_height(n));
  3423. if (mapbool(late_string(n, N_fixed, "false"))) {
  3424. if (sz.x < info->size.x || sz.y < info->size.y) {
  3425. /* should check that the record really won't fit, e.g., there may be no text.
  3426. agwarningf("node '%s' size may be too small\n", agnameof(n));
  3427. */
  3428. }
  3429. } else {
  3430. sz.x = fmax(info->size.x, sz.x);
  3431. sz.y = fmax(info->size.y, sz.y);
  3432. }
  3433. resize_reclbl(info, sz, mapbool(late_string(n, N_nojustify, "false")));
  3434. pointf ul = {-sz.x / 2., sz.y / 2.}; /* FIXME - is this still true: suspected to introduce rounding error - see Kluge below */
  3435. pos_reclbl(info, ul, sides);
  3436. ND_width(n) = PS2INCH(info->size.x);
  3437. ND_height(n) = PS2INCH(info->size.y + 1); /* Kluge!! +1 to fix rounding diff between layout and rendering
  3438. otherwise we can get -1 coords in output */
  3439. ND_shape_info(n) = info;
  3440. }
  3441. static void record_free(node_t * n)
  3442. {
  3443. field_t *p = ND_shape_info(n);
  3444. free_field(p);
  3445. }
  3446. static field_t *map_rec_port(field_t * f, char *str)
  3447. {
  3448. field_t *rv;
  3449. int sub;
  3450. if (f->id && streq(f->id, str))
  3451. rv = f;
  3452. else {
  3453. rv = NULL;
  3454. for (sub = 0; sub < f->n_flds; sub++)
  3455. if ((rv = map_rec_port(f->fld[sub], str)))
  3456. break;
  3457. }
  3458. return rv;
  3459. }
  3460. static port record_port(node_t * n, char *portname, char *compass)
  3461. {
  3462. field_t *f;
  3463. field_t *subf;
  3464. port rv;
  3465. unsigned char sides; // bitmap of which sides the port lies along
  3466. if (portname[0] == '\0')
  3467. return Center;
  3468. sides = BOTTOM | RIGHT | TOP | LEFT;
  3469. if (compass == NULL)
  3470. compass = "_";
  3471. f = ND_shape_info(n);
  3472. if ((subf = map_rec_port(f, portname))) {
  3473. if (compassPort(n, &subf->b, &rv, compass, subf->sides, NULL)) {
  3474. agwarningf(
  3475. "node %s, port %s, unrecognized compass point '%s' - ignored\n",
  3476. agnameof(n), portname, compass);
  3477. }
  3478. } else if (compassPort(n, &f->b, &rv, portname, sides, NULL)) {
  3479. unrecognized(n, portname);
  3480. }
  3481. return rv;
  3482. }
  3483. /* record_inside:
  3484. * Note that this does not handle Mrecords correctly. It assumes
  3485. * everything is a rectangle.
  3486. */
  3487. static bool record_inside(inside_t * inside_context, pointf p)
  3488. {
  3489. field_t *fld0;
  3490. boxf *bp = inside_context->s.bp;
  3491. node_t *n = inside_context->s.n;
  3492. boxf bbox;
  3493. /* convert point to node coordinate system */
  3494. p = ccwrotatepf(p, 90 * GD_rankdir(agraphof(n)));
  3495. if (bp == NULL) {
  3496. fld0 = ND_shape_info(n);
  3497. bbox = fld0->b;
  3498. } else
  3499. bbox = *bp;
  3500. // adjust bbox to outline, i.e., the periphery with penwidth taken into account
  3501. const double penwidth = late_int(n, N_penwidth, DEFAULT_NODEPENWIDTH, MIN_NODEPENWIDTH);
  3502. const pointf extension = {penwidth / 2, penwidth / 2};
  3503. bbox.LL = sub_pointf(bbox.LL, extension);
  3504. bbox.UR = add_pointf(bbox.UR, extension);
  3505. return INSIDE(p, bbox);
  3506. }
  3507. /* record_path:
  3508. * Generate box path from port to border.
  3509. * See poly_path for constraints.
  3510. */
  3511. static int record_path(node_t * n, port * prt, int side, boxf rv[],
  3512. int *kptr)
  3513. {
  3514. int i;
  3515. double ls, rs;
  3516. pointf p;
  3517. field_t *info;
  3518. if (!prt->defined)
  3519. return 0;
  3520. p = prt->p;
  3521. info = ND_shape_info(n);
  3522. for (i = 0; i < info->n_flds; i++) {
  3523. if (!GD_flip(agraphof(n))) {
  3524. ls = info->fld[i]->b.LL.x;
  3525. rs = info->fld[i]->b.UR.x;
  3526. } else {
  3527. ls = info->fld[i]->b.LL.y;
  3528. rs = info->fld[i]->b.UR.y;
  3529. }
  3530. if (BETWEEN(ls, p.x, rs)) {
  3531. /* FIXME: I don't understand this code */
  3532. if (GD_flip(agraphof(n))) {
  3533. rv[0] = flip_rec_boxf(info->fld[i]->b, ND_coord(n));
  3534. } else {
  3535. rv[0].LL.x = ND_coord(n).x + ls;
  3536. rv[0].LL.y = ND_coord(n).y - (ND_ht(n) / 2);
  3537. rv[0].UR.x = ND_coord(n).x + rs;
  3538. }
  3539. rv[0].UR.y = ND_coord(n).y + (ND_ht(n) / 2);
  3540. *kptr = 1;
  3541. break;
  3542. }
  3543. }
  3544. return side;
  3545. }
  3546. static void gen_fields(GVJ_t * job, node_t * n, field_t * f)
  3547. {
  3548. int i;
  3549. pointf AF[2], coord;
  3550. if (f->lp) {
  3551. f->lp->pos = add_pointf(mid_pointf(f->b.LL, f->b.UR), ND_coord(n));
  3552. emit_label(job, EMIT_NLABEL, f->lp);
  3553. penColor(job, n);
  3554. }
  3555. coord = ND_coord(n);
  3556. for (i = 0; i < f->n_flds; i++) {
  3557. if (i > 0) {
  3558. if (f->LR) {
  3559. AF[0] = f->fld[i]->b.LL;
  3560. AF[1].x = AF[0].x;
  3561. AF[1].y = f->fld[i]->b.UR.y;
  3562. } else {
  3563. AF[1] = f->fld[i]->b.UR;
  3564. AF[0].x = f->fld[i]->b.LL.x;
  3565. AF[0].y = AF[1].y;
  3566. }
  3567. AF[0] = add_pointf(AF[0], coord);
  3568. AF[1] = add_pointf(AF[1], coord);
  3569. gvrender_polyline(job, AF, 2);
  3570. }
  3571. gen_fields(job, n, f->fld[i]);
  3572. }
  3573. }
  3574. static void record_gencode(GVJ_t * job, node_t * n)
  3575. {
  3576. obj_state_t *obj = job->obj;
  3577. boxf BF;
  3578. pointf AF[4];
  3579. field_t *f;
  3580. int doMap = obj->url || obj->explicit_tooltip;
  3581. int filled;
  3582. f = ND_shape_info(n);
  3583. BF = f->b;
  3584. BF.LL.x += ND_coord(n).x;
  3585. BF.LL.y += ND_coord(n).y;
  3586. BF.UR.x += ND_coord(n).x;
  3587. BF.UR.y += ND_coord(n).y;
  3588. if (doMap && !(job->flags & EMIT_CLUSTERS_LAST))
  3589. gvrender_begin_anchor(job,
  3590. obj->url, obj->tooltip, obj->target,
  3591. obj->id);
  3592. graphviz_polygon_style_t style = stylenode(job, n);
  3593. penColor(job, n);
  3594. char *clrs[2] = {0};
  3595. if (style.filled) {
  3596. char* fillcolor = findFill (n);
  3597. double frac;
  3598. if (findStopColor (fillcolor, clrs, &frac)) {
  3599. gvrender_set_fillcolor(job, clrs[0]);
  3600. if (clrs[1])
  3601. gvrender_set_gradient_vals(job,clrs[1],late_int(n,N_gradientangle,0,0), frac);
  3602. else
  3603. gvrender_set_gradient_vals(job,DEFAULT_COLOR,late_int(n,N_gradientangle,0,0), frac);
  3604. if (style.radial)
  3605. filled = RGRADIENT;
  3606. else
  3607. filled = GRADIENT;
  3608. }
  3609. else {
  3610. filled = FILL;
  3611. gvrender_set_fillcolor(job, fillcolor);
  3612. }
  3613. }
  3614. else filled = 0;
  3615. if (streq(ND_shape(n)->name, "Mrecord"))
  3616. style.rounded = true;
  3617. if (SPECIAL_CORNERS(style)) {
  3618. AF[0] = BF.LL;
  3619. AF[2] = BF.UR;
  3620. AF[1].x = AF[2].x;
  3621. AF[1].y = AF[0].y;
  3622. AF[3].x = AF[0].x;
  3623. AF[3].y = AF[2].y;
  3624. round_corners(job, AF, 4, style, filled);
  3625. } else {
  3626. gvrender_box(job, BF, filled);
  3627. }
  3628. gen_fields(job, n, f);
  3629. free (clrs[0]);
  3630. free(clrs[1]);
  3631. if (doMap) {
  3632. if (job->flags & EMIT_CLUSTERS_LAST)
  3633. gvrender_begin_anchor(job,
  3634. obj->url, obj->tooltip, obj->target,
  3635. obj->id);
  3636. gvrender_end_anchor(job);
  3637. }
  3638. }
  3639. static shape_desc **UserShape;
  3640. static size_t N_UserShape;
  3641. shape_desc *find_user_shape(const char *name)
  3642. {
  3643. if (UserShape) {
  3644. for (size_t i = 0; i < N_UserShape; i++) {
  3645. if (streq(UserShape[i]->name, name))
  3646. return UserShape[i];
  3647. }
  3648. }
  3649. return NULL;
  3650. }
  3651. static shape_desc *user_shape(char *name)
  3652. {
  3653. shape_desc *p;
  3654. if ((p = find_user_shape(name)))
  3655. return p;
  3656. size_t i = N_UserShape++;
  3657. UserShape = gv_recalloc(UserShape, N_UserShape - 1, N_UserShape, sizeof(shape_desc *));
  3658. p = UserShape[i] = gv_alloc(sizeof(shape_desc));
  3659. *p = Shapes[0];
  3660. p->name = strdup(name);
  3661. if (Lib == NULL && !streq(name, "custom")) {
  3662. agwarningf("using %s for unknown shape %s\n", Shapes[0].name,
  3663. p->name);
  3664. p->usershape = false;
  3665. } else {
  3666. p->usershape = true;
  3667. }
  3668. return p;
  3669. }
  3670. shape_desc *bind_shape(char *name, node_t * np)
  3671. {
  3672. shape_desc *ptr, *rv = NULL;
  3673. const char *str;
  3674. str = safefile(agget(np, "shapefile"));
  3675. /* If shapefile is defined and not epsf, set shape = custom */
  3676. if (str && !streq(name, "epsf"))
  3677. name = "custom";
  3678. if (!streq(name, "custom")) {
  3679. for (ptr = Shapes; ptr->name; ptr++) {
  3680. if (streq(ptr->name, name)) {
  3681. rv = ptr;
  3682. break;
  3683. }
  3684. }
  3685. }
  3686. if (rv == NULL)
  3687. rv = user_shape(name);
  3688. return rv;
  3689. }
  3690. static bool epsf_inside(inside_t * inside_context, pointf p)
  3691. {
  3692. pointf P;
  3693. double x2;
  3694. node_t *n = inside_context->s.n;
  3695. P = ccwrotatepf(p, 90 * GD_rankdir(agraphof(n)));
  3696. x2 = ND_ht(n) / 2;
  3697. return P.y >= -x2 && P.y <= x2 && P.x >= -ND_lw(n) && P.x <= ND_rw(n);
  3698. }
  3699. static void epsf_gencode(GVJ_t * job, node_t * n)
  3700. {
  3701. obj_state_t *obj = job->obj;
  3702. epsf_t *desc;
  3703. int doMap = obj->url || obj->explicit_tooltip;
  3704. desc = ND_shape_info(n);
  3705. if (!desc)
  3706. return;
  3707. if (doMap && !(job->flags & EMIT_CLUSTERS_LAST))
  3708. gvrender_begin_anchor(job,
  3709. obj->url, obj->tooltip, obj->target,
  3710. obj->id);
  3711. if (desc)
  3712. fprintf(job->output_file,
  3713. "%.5g %.5g translate newpath user_shape_%d\n",
  3714. ND_coord(n).x + desc->offset.x,
  3715. ND_coord(n).y + desc->offset.y, desc->macro_id);
  3716. ND_label(n)->pos = ND_coord(n);
  3717. emit_label(job, EMIT_NLABEL, ND_label(n));
  3718. if (doMap) {
  3719. if (job->flags & EMIT_CLUSTERS_LAST)
  3720. gvrender_begin_anchor(job,
  3721. obj->url, obj->tooltip, obj->target,
  3722. obj->id);
  3723. gvrender_end_anchor(job);
  3724. }
  3725. }
  3726. #define alpha (M_PI/10.0)
  3727. #define alpha2 (2*alpha)
  3728. #define alpha3 (3*alpha)
  3729. #define alpha4 (2*alpha2)
  3730. static pointf star_size (pointf sz0)
  3731. {
  3732. pointf sz;
  3733. double r, rx, ry;
  3734. rx = sz0.x/(2*cos(alpha));
  3735. ry = sz0.y/(sin(alpha) + sin(alpha3));
  3736. const double r0 = fmax(rx, ry);
  3737. r = r0 * sin(alpha4) * cos(alpha2) / (cos(alpha) * cos(alpha4));
  3738. sz.x = 2*r*cos(alpha);
  3739. sz.y = r*(1 + sin(alpha3));
  3740. return sz;
  3741. }
  3742. static void star_vertices (pointf* vertices, pointf* bb)
  3743. {
  3744. int i;
  3745. pointf sz = *bb;
  3746. double offset, a, aspect = (1 + sin(alpha3))/(2*cos(alpha));
  3747. double r, r0, theta = alpha;
  3748. /* Scale up width or height to required aspect ratio */
  3749. a = sz.y/sz.x;
  3750. if (a > aspect) {
  3751. sz.x = sz.y/aspect;
  3752. }
  3753. else if (a < aspect) {
  3754. sz.y = sz.x*aspect;
  3755. }
  3756. /* for given sz, get radius */
  3757. r = sz.x/(2*cos(alpha));
  3758. r0 = r * cos(alpha) * cos(alpha4) / (sin(alpha4) * cos(alpha2));
  3759. /* offset is the y shift of circle center from bb center */
  3760. offset = (r*(1 - sin(alpha3)))/2;
  3761. for (i = 0; i < 10; i += 2) {
  3762. vertices[i].x = r*cos(theta);
  3763. vertices[i].y = r*sin(theta) - offset;
  3764. theta += alpha2;
  3765. vertices[i+1].x = r0*cos(theta);
  3766. vertices[i+1].y = r0*sin(theta) - offset;
  3767. theta += alpha2;
  3768. }
  3769. *bb = sz;
  3770. }
  3771. static bool star_inside(inside_t * inside_context, pointf p)
  3772. {
  3773. size_t sides;
  3774. pointf *vertex;
  3775. const pointf O = {0};
  3776. if (!inside_context) {
  3777. return false;
  3778. }
  3779. boxf *bp = inside_context->s.bp;
  3780. node_t *n = inside_context->s.n;
  3781. pointf P, Q, R;
  3782. int outcnt;
  3783. P = ccwrotatepf(p, 90 * GD_rankdir(agraphof(n)));
  3784. /* Quick test if port rectangle is target */
  3785. if (bp) {
  3786. boxf bbox = *bp;
  3787. return INSIDE(P, bbox);
  3788. }
  3789. if (n != inside_context->s.lastn) {
  3790. inside_context->s.last_poly = ND_shape_info(n);
  3791. vertex = inside_context->s.last_poly->vertices;
  3792. sides = inside_context->s.last_poly->sides;
  3793. const double penwidth = late_int(n, N_penwidth, DEFAULT_NODEPENWIDTH, MIN_NODEPENWIDTH);
  3794. if (inside_context->s.last_poly->peripheries >= 1 && penwidth > 0) {
  3795. /* index to outline, i.e., the outer-periphery with penwidth taken into account */
  3796. inside_context->s.outp = (inside_context->s.last_poly->peripheries + 1 - 1)
  3797. * sides;
  3798. } else if (inside_context->s.last_poly->peripheries < 1) {
  3799. inside_context->s.outp = 0;
  3800. } else {
  3801. /* index to outer-periphery */
  3802. inside_context->s.outp = (inside_context->s.last_poly->peripheries - 1)
  3803. * sides;
  3804. }
  3805. inside_context->s.lastn = n;
  3806. } else {
  3807. vertex = inside_context->s.last_poly->vertices;
  3808. sides = inside_context->s.last_poly->sides;
  3809. }
  3810. outcnt = 0;
  3811. for (size_t i = 0; i < sides; i += 2) {
  3812. Q = vertex[i + inside_context->s.outp];
  3813. R = vertex[(i + 4) % sides + inside_context->s.outp];
  3814. if (!(same_side(P, O, Q, R))) {
  3815. outcnt++;
  3816. }
  3817. if (outcnt == 2) {
  3818. return false;
  3819. }
  3820. }
  3821. return true;
  3822. }
  3823. /* cylinder:
  3824. * Code based on PostScript version by Brandon Rhodes.
  3825. * http://rhodesmill.org/brandon/2007/a-database-symbol-for-graphviz/
  3826. */
  3827. static pointf cylinder_size (pointf sz)
  3828. {
  3829. sz.y *= 1.375;
  3830. return sz;
  3831. }
  3832. static void cylinder_vertices (pointf* vertices, pointf* bb)
  3833. {
  3834. double x = bb->x/2;
  3835. double y = bb->y/2;
  3836. double yr = bb->y/11;
  3837. vertices[0].x = x;
  3838. vertices[0].y = y-yr;
  3839. vertices[1].x = x;
  3840. vertices[1].y = y-(1-0.551784)*yr;
  3841. vertices[2].x = 0.551784*x;
  3842. vertices[2].y = y;
  3843. vertices[3].x = 0;
  3844. vertices[3].y = y;
  3845. vertices[4].x = -0.551784*x;
  3846. vertices[4].y = y;
  3847. vertices[5].x = -x;
  3848. vertices[5].y = vertices[1].y;
  3849. vertices[6].x = -x;
  3850. vertices[6].y = y-yr;
  3851. vertices[7] = vertices[6];
  3852. vertices[8].x = -x;
  3853. vertices[8].y = yr-y;
  3854. vertices[9] = vertices[8];
  3855. vertices[10].x = -x;
  3856. vertices[10].y = -vertices[1].y;
  3857. vertices[11].x = vertices[4].x;
  3858. vertices[11].y = -vertices[4].y;
  3859. vertices[12].x = vertices[3].x;
  3860. vertices[12].y = -vertices[3].y;
  3861. vertices[13].x = vertices[2].x;
  3862. vertices[13].y = -vertices[2].y;
  3863. vertices[14].x = vertices[1].x;
  3864. vertices[14].y = -vertices[1].y;
  3865. vertices[15].x = vertices[0].x;
  3866. vertices[15].y = -vertices[0].y;
  3867. vertices[16] = vertices[15];
  3868. vertices[18] = vertices[17] = vertices[0];
  3869. }
  3870. static void cylinder_draw(GVJ_t *job, pointf *AF, size_t sides, int filled) {
  3871. pointf vertices[7];
  3872. double y0 = AF[0].y;
  3873. double y02 = y0+y0;
  3874. vertices[0] = AF[0];
  3875. vertices[1].x = AF[1].x;
  3876. vertices[1].y = y02 - AF[1].y;
  3877. vertices[2].x = AF[2].x;
  3878. vertices[2].y = y02 - AF[2].y;
  3879. vertices[3].x = AF[3].x;
  3880. vertices[3].y = y02 - AF[3].y;
  3881. vertices[4].x = AF[4].x;
  3882. vertices[4].y = y02 - AF[4].y;
  3883. vertices[5].x = AF[5].x;
  3884. vertices[5].y = y02 - AF[5].y;
  3885. vertices[6] = AF[6];
  3886. gvrender_beziercurve(job, AF, sides, filled);
  3887. gvrender_beziercurve(job, vertices, 7, 0);
  3888. }
  3889. static const char *side_port[] = {"s", "e", "n", "w"};
  3890. static pointf cvtPt(pointf p, int rankdir) {
  3891. pointf q = { 0, 0 };
  3892. switch (rankdir) {
  3893. case RANKDIR_TB:
  3894. q = p;
  3895. break;
  3896. case RANKDIR_BT:
  3897. q.x = p.x;
  3898. q.y = -p.y;
  3899. break;
  3900. case RANKDIR_LR:
  3901. q.y = p.x;
  3902. q.x = -p.y;
  3903. break;
  3904. case RANKDIR_RL:
  3905. q.y = p.x;
  3906. q.x = p.y;
  3907. break;
  3908. default:
  3909. UNREACHABLE();
  3910. }
  3911. return q;
  3912. }
  3913. /* closestSide:
  3914. * Resolve unspecified compass-point port to best available port.
  3915. * At present, this finds the available side closest to the center
  3916. * of the other port.
  3917. *
  3918. * This could be improved:
  3919. * - if other is unspecified, do them together
  3920. * - if dot, bias towards bottom of one to top of another, if possible
  3921. * - if line segment from port centers uses available sides, use these
  3922. * or center. (This latter may require spline routing to cooperate.)
  3923. */
  3924. static const char *closestSide(node_t *n, node_t *other, port *oldport) {
  3925. boxf b;
  3926. int rkd = GD_rankdir(agraphof(n)->root);
  3927. pointf p = {0};
  3928. const pointf pt = cvtPt(ND_coord(n), rkd);
  3929. const pointf opt = cvtPt(ND_coord(other), rkd);
  3930. int sides = oldport->side;
  3931. const char *rv = NULL;
  3932. if (sides == 0 || sides == (TOP | BOTTOM | LEFT | RIGHT))
  3933. return rv; /* use center */
  3934. if (oldport->bp) {
  3935. b = *oldport->bp;
  3936. } else {
  3937. if (GD_flip(agraphof(n))) {
  3938. b.UR.x = ND_ht(n) / 2;
  3939. b.LL.x = -b.UR.x;
  3940. b.UR.y = ND_lw(n);
  3941. b.LL.y = -b.UR.y;
  3942. } else {
  3943. b.UR.y = ND_ht(n) / 2;
  3944. b.LL.y = -b.UR.y;
  3945. b.UR.x = ND_lw(n);
  3946. b.LL.x = -b.UR.x;
  3947. }
  3948. }
  3949. double mind = 0;
  3950. for (int i = 0; i < 4; i++) {
  3951. if ((sides & (1 << i)) == 0)
  3952. continue;
  3953. switch (i) {
  3954. case BOTTOM_IX:
  3955. p.y = b.LL.y;
  3956. p.x = (b.LL.x + b.UR.x) / 2;
  3957. break;
  3958. case RIGHT_IX:
  3959. p.x = b.UR.x;
  3960. p.y = (b.LL.y + b.UR.y) / 2;
  3961. break;
  3962. case TOP_IX:
  3963. p.y = b.UR.y;
  3964. p.x = (b.LL.x + b.UR.x) / 2;
  3965. break;
  3966. case LEFT_IX:
  3967. p.x = b.LL.x;
  3968. p.y = (b.LL.y + b.UR.y) / 2;
  3969. break;
  3970. default:
  3971. UNREACHABLE();
  3972. }
  3973. p.x += pt.x;
  3974. p.y += pt.y;
  3975. const double d = DIST2(p, opt);
  3976. if (!rv || d < mind) {
  3977. mind = d;
  3978. rv = side_port[i];
  3979. }
  3980. }
  3981. return rv;
  3982. }
  3983. port resolvePort(node_t * n, node_t * other, port * oldport)
  3984. {
  3985. port rv;
  3986. const char *compass = closestSide(n, other, oldport);
  3987. /* transfer name pointer; all other necessary fields will be regenerated */
  3988. rv.name = oldport->name;
  3989. compassPort(n, oldport->bp, &rv, compass, oldport->side, NULL);
  3990. return rv;
  3991. }
  3992. void resolvePorts(edge_t * e)
  3993. {
  3994. if (ED_tail_port(e).dyna)
  3995. ED_tail_port(e) =
  3996. resolvePort(agtail(e), aghead(e), &ED_tail_port(e));
  3997. if (ED_head_port(e).dyna)
  3998. ED_head_port(e) =
  3999. resolvePort(aghead(e), agtail(e), &ED_head_port(e));
  4000. }