ChartGraphics3D.cs 163 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. //
  5. // Purpose: ChartGraphics3D class is 3D chart rendering engine.
  6. // All chart 3D shapes are drawn in specific order so
  7. // that correct Z order of all shapes is achieved. 3D
  8. // graphics engine do not support shapes intersection.
  9. // 3D shapes are transformed into one or more 2D shapes
  10. // and then drawn with 2D chart graphics engine.
  11. //
  12. using System;
  13. using System.Collections;
  14. using System.ComponentModel;
  15. using System.Diagnostics.CodeAnalysis;
  16. using System.Drawing;
  17. using System.Drawing.Drawing2D;
  18. namespace FastReport.DataVisualization.Charting
  19. {
  20. #region 3D enumerations
  21. /// <summary>
  22. /// 3D cube surfaces names.
  23. /// </summary>
  24. [Flags]
  25. internal enum SurfaceNames
  26. {
  27. /// <summary>
  28. /// Front.
  29. /// </summary>
  30. Front = 1,
  31. /// <summary>
  32. /// Back.
  33. /// </summary>
  34. Back = 2,
  35. /// <summary>
  36. /// Left.
  37. /// </summary>
  38. Left = 4,
  39. /// <summary>
  40. /// Right.
  41. /// </summary>
  42. Right = 8,
  43. /// <summary>
  44. /// Top.
  45. /// </summary>
  46. Top = 16,
  47. /// <summary>
  48. /// Bottom.
  49. /// </summary>
  50. Bottom = 32
  51. }
  52. /// <summary>
  53. /// This enumeration defines all significant points in a pie
  54. /// slice. Only these points should be transformed for pie
  55. /// chart using Matrix object.
  56. /// </summary>
  57. internal enum PiePoints
  58. {
  59. /// <summary>
  60. /// Angle 180 Top point on the arc
  61. /// </summary>
  62. Top180,
  63. /// <summary>
  64. /// Angle 180 Bottom point on the arc
  65. /// </summary>
  66. Bottom180,
  67. /// <summary>
  68. /// Angle 0 Top point on the arc
  69. /// </summary>
  70. Top0,
  71. /// <summary>
  72. /// Angle 0 Bottom point on the arc
  73. /// </summary>
  74. Bottom0,
  75. /// <summary>
  76. /// Top Start Angle point on the arc
  77. /// </summary>
  78. TopStart,
  79. /// <summary>
  80. /// Top End Angle point on the arc
  81. /// </summary>
  82. TopEnd,
  83. /// <summary>
  84. /// Bottom Start Angle point on the arc
  85. /// </summary>
  86. BottomStart,
  87. /// <summary>
  88. /// Bottom End Angle point on the arc
  89. /// </summary>
  90. BottomEnd,
  91. /// <summary>
  92. /// Center Top
  93. /// </summary>
  94. TopCenter,
  95. /// <summary>
  96. /// Center Bottom
  97. /// </summary>
  98. BottomCenter,
  99. /// <summary>
  100. /// Top Label Line
  101. /// </summary>
  102. TopLabelLine,
  103. /// <summary>
  104. /// Top Label Line Out
  105. /// </summary>
  106. TopLabelLineout,
  107. /// <summary>
  108. /// Top Label Center
  109. /// </summary>
  110. TopLabelCenter,
  111. /// <summary>
  112. /// Top Rectangle Top Left Point
  113. /// </summary>
  114. TopRectTopLeftPoint,
  115. /// <summary>
  116. /// Top Rectangle Right Bottom Point
  117. /// </summary>
  118. TopRectBottomRightPoint,
  119. /// <summary>
  120. /// Bottom Rectangle Top Left Point
  121. /// </summary>
  122. BottomRectTopLeftPoint,
  123. /// <summary>
  124. /// Bottom Rectangle Right Bottom Point
  125. /// </summary>
  126. BottomRectBottomRightPoint,
  127. /// <summary>
  128. /// Angle 180 Top point on the Doughnut arc
  129. /// </summary>
  130. DoughnutTop180,
  131. /// <summary>
  132. /// Angle 180 Bottom point on the Doughnut arc
  133. /// </summary>
  134. DoughnutBottom180,
  135. /// <summary>
  136. /// Angle 0 Top point on the Doughnut arc
  137. /// </summary>
  138. DoughnutTop0,
  139. /// <summary>
  140. /// Angle 0 Bottom point on the Doughnut arc
  141. /// </summary>
  142. DoughnutBottom0,
  143. /// <summary>
  144. /// Top Start Angle point on the Doughnut arc
  145. /// </summary>
  146. DoughnutTopStart,
  147. /// <summary>
  148. /// Top End Angle point on the Doughnut arc
  149. /// </summary>
  150. DoughnutTopEnd,
  151. /// <summary>
  152. /// Bottom Start Angle point on the Doughnut arc
  153. /// </summary>
  154. DoughnutBottomStart,
  155. /// <summary>
  156. /// Bottom End Angle point on the Doughnut arc
  157. /// </summary>
  158. DoughnutBottomEnd,
  159. /// <summary>
  160. /// Doughnut Top Rectangle Top Left Point
  161. /// </summary>
  162. DoughnutTopRectTopLeftPoint,
  163. /// <summary>
  164. /// Doughnut Top Rectangle Right Bottom Point
  165. /// </summary>
  166. DoughnutTopRectBottomRightPoint,
  167. /// <summary>
  168. /// Doughnut Bottom Rectangle Top Left Point
  169. /// </summary>
  170. DoughnutBottomRectTopLeftPoint,
  171. /// <summary>
  172. /// Doughnut Bottom Rectangle Right Bottom Point
  173. /// </summary>
  174. DoughnutBottomRectBottomRightPoint,
  175. }
  176. /// <summary>
  177. /// AxisName of drawing operation.
  178. /// </summary>
  179. [Flags]
  180. internal enum DrawingOperationTypes
  181. {
  182. /// <summary>
  183. /// Draw element.
  184. /// </summary>
  185. DrawElement = 1,
  186. /// <summary>
  187. /// Calculate element path. (for selection or tooltips)
  188. /// </summary>
  189. CalcElementPath = 2,
  190. }
  191. /// <summary>
  192. /// AxisName of line segment.
  193. /// </summary>
  194. internal enum LineSegmentType
  195. {
  196. /// <summary>
  197. /// Only one segment exists.
  198. /// </summary>
  199. Single,
  200. /// <summary>
  201. /// First segment.
  202. /// </summary>
  203. First,
  204. /// <summary>
  205. /// Middle segment.
  206. /// </summary>
  207. Middle,
  208. /// <summary>
  209. /// Last segment.
  210. /// </summary>
  211. Last
  212. }
  213. #endregion
  214. /// <summary>
  215. /// The ChartGraphics class is 3D chart rendering engine. All chart
  216. /// 3D shapes are drawn in specific order so that correct Z order
  217. /// of all shapes is achieved. 3D graphics engine do not support
  218. /// shapes intersection. 3D shapes are transformed into one or
  219. /// more 2D shapes and then drawn with 2D chart graphics engine.
  220. /// </summary>
  221. public partial class ChartGraphics
  222. {
  223. #region Fields
  224. /// <summary>
  225. /// Helper field used to store the index of cylinder left/bottom side coordinate.
  226. /// </summary>
  227. private int _oppLeftBottomPoint = -1;
  228. /// <summary>
  229. /// Helper field used to store the index of cylinder right/top side coordinate.
  230. /// </summary>
  231. private int _oppRigthTopPoint = -1;
  232. /// <summary>
  233. /// Point of the front line from the previous line segment.
  234. /// </summary>
  235. internal PointF frontLinePoint1 = PointF.Empty;
  236. /// <summary>
  237. /// Point of the front line from the previous line segment.
  238. /// </summary>
  239. internal PointF frontLinePoint2 = PointF.Empty;
  240. /// <summary>
  241. /// Previous line segment pen.
  242. /// </summary>
  243. internal Pen frontLinePen = null;
  244. #endregion
  245. #region 3D Line drawing methods
  246. /// <summary>
  247. /// Draws grid line in 3D space (on two area scene walls)
  248. /// </summary>
  249. /// <param name="area">Chart area.</param>
  250. /// <param name="color">Line color.</param>
  251. /// <param name="width">Line width.</param>
  252. /// <param name="style">Line style.</param>
  253. /// <param name="point1">First line point.</param>
  254. /// <param name="point2">Second line point.</param>
  255. /// <param name="horizontal">Indicates that grid line is horizontal</param>
  256. /// <param name="common">Common Elements</param>
  257. /// <param name="obj">Selected object</param>
  258. internal void Draw3DGridLine(
  259. ChartArea area,
  260. Color color,
  261. int width,
  262. ChartDashStyle style,
  263. PointF point1,
  264. PointF point2,
  265. bool horizontal,
  266. CommonElements common,
  267. object obj
  268. )
  269. {
  270. float zPositon = area.IsMainSceneWallOnFront() ? area.areaSceneDepth : 0f;
  271. ChartElementType chartElementType = obj is StripLine ? ChartElementType.StripLines : ChartElementType.Gridlines;
  272. // Draw strip line on the back/front wall
  273. ((ChartGraphics)this).Draw3DLine(
  274. area.matrix3D,
  275. color, width, style,
  276. new Point3D(point1.X, point1.Y, zPositon),
  277. new Point3D(point2.X, point2.Y, zPositon),
  278. common,
  279. obj,
  280. chartElementType
  281. );
  282. if(horizontal)
  283. {
  284. // Draw strip line on the side wall (left or right)
  285. if(area.IsSideSceneWallOnLeft())
  286. {
  287. point1.X = Math.Min(point1.X, point2.X);
  288. }
  289. else
  290. {
  291. point1.X = Math.Max(point1.X, point2.X);
  292. }
  293. ((ChartGraphics)this).Draw3DLine(
  294. area.matrix3D,
  295. color, width, style,
  296. new Point3D(point1.X, point1.Y, 0f),
  297. new Point3D(point1.X, point1.Y, area.areaSceneDepth),
  298. common,
  299. obj,
  300. chartElementType
  301. );
  302. }
  303. else if(area.IsBottomSceneWallVisible())
  304. {
  305. // Draw strip line on the bottom wall (if visible)
  306. point1.Y = Math.Max(point1.Y, point2.Y);
  307. ((ChartGraphics)this).Draw3DLine(
  308. area.matrix3D,
  309. color, width, style,
  310. new Point3D(point1.X, point1.Y, 0f),
  311. new Point3D(point1.X, point1.Y, area.areaSceneDepth),
  312. common,
  313. obj,
  314. chartElementType
  315. );
  316. }
  317. }
  318. /// <summary>
  319. /// Draws a line connecting the two specified points.
  320. /// </summary>
  321. /// <param name="matrix">Coordinates transformation matrix.</param>
  322. /// <param name="color">Line color.</param>
  323. /// <param name="width">Line width.</param>
  324. /// <param name="style">Line style.</param>
  325. /// <param name="firstPoint">A Point that represents the first point to connect.</param>
  326. /// <param name="secondPoint">A Point that represents the second point to connect.</param>
  327. /// <param name="common">Common elements</param>
  328. /// <param name="obj">Selected object</param>
  329. /// <param name="type">Selected chart element</param>
  330. internal void Draw3DLine(
  331. Matrix3D matrix,
  332. Color color,
  333. int width,
  334. ChartDashStyle style,
  335. Point3D firstPoint,
  336. Point3D secondPoint,
  337. CommonElements common,
  338. object obj,
  339. ChartElementType type
  340. )
  341. {
  342. // Transform coordinates
  343. Point3D [] points = new Point3D[] {firstPoint, secondPoint};
  344. matrix.TransformPoints( points );
  345. // Selection mode
  346. if (common.ProcessModeRegions && type != ChartElementType.Nothing)
  347. {
  348. using (GraphicsPath path = new GraphicsPath())
  349. {
  350. if (Math.Abs(points[0].X - points[1].X) > Math.Abs(points[0].Y - points[1].Y))
  351. {
  352. path.AddLine(points[0].X, points[0].Y - 1, points[1].X, points[1].Y - 1);
  353. path.AddLine(points[1].X, points[1].Y + 1, points[0].X, points[0].Y + 1);
  354. path.CloseAllFigures();
  355. }
  356. else
  357. {
  358. path.AddLine(points[0].X - 1, points[0].Y, points[1].X - 1, points[1].Y);
  359. path.AddLine(points[1].X + 1, points[1].Y, points[0].X + 1, points[0].Y);
  360. path.CloseAllFigures();
  361. }
  362. common.HotRegionsList.AddHotRegion(path, true, type, obj);
  363. }
  364. }
  365. if( common.ProcessModePaint )
  366. {
  367. // Draw 2D line in 3D space
  368. ((ChartGraphics)this).DrawLineRel(color, width, style, points[0].PointF, points[1].PointF);
  369. }
  370. }
  371. #endregion
  372. #region 3D Pie Drawing methods and enumerations
  373. /// <summary>
  374. /// This method draw and fill four point polygons which
  375. /// represents sides of a pie slice.
  376. /// </summary>
  377. /// <param name="area">Chart Area</param>
  378. /// <param name="inclination">X angle rotation</param>
  379. /// <param name="startAngle">Start Angle of a pie slice</param>
  380. /// <param name="sweepAngle">Sweep angle of a pie slice</param>
  381. /// <param name="points">Significant points of a pie slice</param>
  382. /// <param name="brush">Brush used for fill</param>
  383. /// <param name="pen">Pen used for drawing</param>
  384. /// <param name="doughnut">Chart AxisName is Doughnut</param>
  385. internal void FillPieSides(
  386. ChartArea area,
  387. float inclination,
  388. float startAngle,
  389. float sweepAngle,
  390. PointF [] points,
  391. SolidBrush brush,
  392. Pen pen,
  393. bool doughnut
  394. )
  395. {
  396. // Create a graphics path
  397. GraphicsPath path = new GraphicsPath();
  398. // Significant Points for Side polygons
  399. PointF topCenter = points[(int)PiePoints.TopCenter];
  400. PointF bottomCenter = points[(int)PiePoints.BottomCenter];
  401. PointF topStart = points[(int)PiePoints.TopStart];
  402. PointF bottomStart = points[(int)PiePoints.BottomStart];
  403. PointF topEnd = points[(int)PiePoints.TopEnd];
  404. PointF bottomEnd = points[(int)PiePoints.BottomEnd];
  405. // For Doughnut
  406. PointF topDoughnutStart = PointF.Empty;
  407. PointF bottomDoughnutStart = PointF.Empty;
  408. PointF topDoughnutEnd = PointF.Empty;
  409. PointF bottomDoughnutEnd = PointF.Empty;
  410. if( doughnut )
  411. {
  412. // For Doughnut
  413. topDoughnutStart = points[(int)PiePoints.DoughnutTopStart];
  414. bottomDoughnutStart = points[(int)PiePoints.DoughnutBottomStart];
  415. topDoughnutEnd = points[(int)PiePoints.DoughnutTopEnd];
  416. bottomDoughnutEnd = points[(int)PiePoints.DoughnutBottomEnd];
  417. }
  418. bool startSide = false;
  419. bool endSide = false;
  420. float endAngle = startAngle + sweepAngle;
  421. // If X angle is negative different side of pie slice is visible.
  422. if (inclination > 0)
  423. {
  424. // Enable start or/and the end side
  425. if( startAngle > -90 && startAngle < 90 || startAngle > 270 && startAngle < 450 )
  426. {
  427. startSide = true;
  428. }
  429. if( endAngle >= -180 && endAngle < -90 || endAngle > 90 && endAngle < 270 || endAngle > 450 && endAngle <= 540 )
  430. {
  431. endSide = true;
  432. }
  433. }
  434. else
  435. {
  436. // Enable start or/and the end side
  437. if( startAngle >= -180 && startAngle < -90 || startAngle > 90 && startAngle < 270 || startAngle > 450 && startAngle <= 540 )
  438. {
  439. startSide = true;
  440. }
  441. if( endAngle > -90 && endAngle < 90 || endAngle > 270 && endAngle < 450 )
  442. {
  443. endSide = true;
  444. }
  445. }
  446. if( startSide )
  447. {
  448. // *****************************************
  449. // Draw Start Angle side
  450. // *****************************************
  451. using (path = new GraphicsPath())
  452. {
  453. if (doughnut)
  454. {
  455. // Add Line between The Doughnut Arc and Arc
  456. path.AddLine(topDoughnutStart, topStart);
  457. // Add Line between The Top Start and Bottom Start
  458. path.AddLine(topStart, bottomStart);
  459. // Add Line between The Bottom Start and Doughnut Start
  460. path.AddLine(bottomStart, bottomDoughnutStart);
  461. // Add Line between The Bottom Doughnut Start and The Top Doughnut Start
  462. path.AddLine(bottomDoughnutStart, topDoughnutStart);
  463. }
  464. else
  465. {
  466. // Add Line between The Center and Arc
  467. path.AddLine(topCenter, topStart);
  468. // Add Line between The Top Start and Bottom Start
  469. path.AddLine(topStart, bottomStart);
  470. // Add Line between The Bottom Start and The Center Bottom
  471. path.AddLine(bottomStart, bottomCenter);
  472. // Add Line between The Bottom Center and The Top Center
  473. path.AddLine(bottomCenter, topCenter);
  474. }
  475. // Get surface colors
  476. Color frontLightColor, leftLightColor, topLightColor, backLightColor, rightLightColor, bottomLightColor;
  477. area.matrix3D.GetLight(brush.Color, out frontLightColor, out backLightColor, out leftLightColor, out rightLightColor, out topLightColor, out bottomLightColor);
  478. Color lightColor;
  479. if (area.Area3DStyle.Inclination < 0)
  480. {
  481. lightColor = bottomLightColor;
  482. }
  483. else
  484. {
  485. lightColor = topLightColor;
  486. }
  487. // Draw Path
  488. using (Brush lightBrush = new SolidBrush(lightColor))
  489. {
  490. FillPath(lightBrush, path);
  491. }
  492. DrawGraphicsPath(pen, path);
  493. }
  494. }
  495. if( endSide )
  496. {
  497. // *****************************************
  498. // Draw End Angle side
  499. // *****************************************
  500. using (path = new GraphicsPath())
  501. {
  502. if (doughnut)
  503. {
  504. // Add Line between The Doughnut Arc and Arc
  505. path.AddLine(topDoughnutEnd, topEnd);
  506. // Add Line between The Top End and Bottom End
  507. path.AddLine(topEnd, bottomEnd);
  508. // Add Line between The Bottom End and Doughnut End
  509. path.AddLine(bottomEnd, bottomDoughnutEnd);
  510. // Add Line between The Bottom Doughnut End and The Top Doughnut End
  511. path.AddLine(bottomDoughnutEnd, topDoughnutEnd);
  512. }
  513. else
  514. {
  515. // Add Line between The Center and Arc
  516. path.AddLine(topCenter, topEnd);
  517. // Add Line between The Top End and Bottom End
  518. path.AddLine(topEnd, bottomEnd);
  519. // Add Line between The Bottom End and The Center Bottom
  520. path.AddLine(bottomEnd, bottomCenter);
  521. // Add Line between The Bottom Center and The Top Center
  522. path.AddLine(bottomCenter, topCenter);
  523. }
  524. // Get surface colors
  525. Color frontLightColor, leftLightColor, topLightColor, backLightColor, rightLightColor, bottomLightColor;
  526. area.matrix3D.GetLight(brush.Color, out frontLightColor, out backLightColor, out leftLightColor, out rightLightColor, out topLightColor, out bottomLightColor);
  527. Color lightColor;
  528. if (area.Area3DStyle.Inclination < 0)
  529. {
  530. lightColor = bottomLightColor;
  531. }
  532. else
  533. {
  534. lightColor = topLightColor;
  535. }
  536. // Draw Path
  537. using (Brush lightBrush = new SolidBrush(lightColor))
  538. {
  539. FillPath(lightBrush, path);
  540. }
  541. DrawGraphicsPath(pen, path);
  542. }
  543. }
  544. }
  545. /// <summary>
  546. /// This method Draw and fill pie curves.
  547. /// </summary>
  548. /// <param name="area">Chart area used for drawing</param>
  549. /// <param name="point">Data Point</param>
  550. /// <param name="brush">Graphic Brush used for drawing</param>
  551. /// <param name="pen">Graphic Pen used for drawing</param>
  552. /// <param name="topFirstRectPoint">Rotated bounded rectangle points</param>
  553. /// <param name="topSecondRectPoint">Rotated bounded rectangle points</param>
  554. /// <param name="bottomFirstRectPoint">Rotated bounded rectangle points</param>
  555. /// <param name="bottomSecondRectPoint">Rotated bounded rectangle points</param>
  556. /// <param name="topFirstPoint">Significant pie points</param>
  557. /// <param name="topSecondPoint">Significant pie points</param>
  558. /// <param name="bottomFirstPoint">Significant pie points</param>
  559. /// <param name="bottomSecondPoint">Significant pie points</param>
  560. /// <param name="startAngle">Start pie angle</param>
  561. /// <param name="sweepAngle">End pie angle</param>
  562. /// <param name="pointIndex">Data Point Index</param>
  563. internal void FillPieCurve(
  564. ChartArea area,
  565. DataPoint point,
  566. Brush brush,
  567. Pen pen,
  568. PointF topFirstRectPoint,
  569. PointF topSecondRectPoint,
  570. PointF bottomFirstRectPoint,
  571. PointF bottomSecondRectPoint,
  572. PointF topFirstPoint,
  573. PointF topSecondPoint,
  574. PointF bottomFirstPoint,
  575. PointF bottomSecondPoint,
  576. float startAngle,
  577. float sweepAngle,
  578. int pointIndex
  579. )
  580. {
  581. // Common Elements
  582. CommonElements common = area.Common;
  583. // Create a graphics path
  584. using (GraphicsPath path = new GraphicsPath())
  585. {
  586. // It is enough to transform only two points from
  587. // rectangle. This code will create RectangleF from
  588. // top left and bottom right points.
  589. RectangleF pieTopRectangle = new RectangleF();
  590. pieTopRectangle.X = topFirstRectPoint.X;
  591. pieTopRectangle.Y = topFirstRectPoint.Y;
  592. pieTopRectangle.Height = topSecondRectPoint.Y - topFirstRectPoint.Y;
  593. pieTopRectangle.Width = topSecondRectPoint.X - topFirstRectPoint.X;
  594. RectangleF pieBottomRectangle = new RectangleF();
  595. pieBottomRectangle.X = bottomFirstRectPoint.X;
  596. pieBottomRectangle.Y = bottomFirstRectPoint.Y;
  597. pieBottomRectangle.Height = bottomSecondRectPoint.Y - bottomFirstRectPoint.Y;
  598. pieBottomRectangle.Width = bottomSecondRectPoint.X - bottomFirstRectPoint.X;
  599. // Angle correction algorithm. After rotation AddArc method should used
  600. // different transformed angles. This method transforms angles.
  601. double angleCorrection = pieTopRectangle.Height / pieTopRectangle.Width;
  602. float endAngle;
  603. endAngle = AngleCorrection(startAngle + sweepAngle, angleCorrection);
  604. startAngle = AngleCorrection(startAngle, angleCorrection);
  605. sweepAngle = endAngle - startAngle;
  606. // Add Line between first points
  607. path.AddLine(topFirstPoint, bottomFirstPoint);
  608. if (pieBottomRectangle.Height <= 0)
  609. {
  610. // If x angle is 0 this arc will be line in projection.
  611. path.AddLine(bottomFirstPoint.X, bottomFirstPoint.Y, bottomSecondPoint.X, bottomSecondPoint.Y);
  612. }
  613. else
  614. {
  615. // Add Arc
  616. path.AddArc(pieBottomRectangle.X, pieBottomRectangle.Y, pieBottomRectangle.Width, pieBottomRectangle.Height, startAngle, sweepAngle);
  617. }
  618. // Add Line between second points
  619. path.AddLine(bottomSecondPoint, topSecondPoint);
  620. if (pieTopRectangle.Height <= 0)
  621. {
  622. // If x angle is 0 this arc will be line in projection.
  623. path.AddLine(topFirstPoint.X, topFirstPoint.Y, topSecondPoint.X, topSecondPoint.Y);
  624. }
  625. else
  626. {
  627. path.AddArc(pieTopRectangle.X, pieTopRectangle.Y, pieTopRectangle.Width, pieTopRectangle.Height, startAngle + sweepAngle, -sweepAngle);
  628. }
  629. if (common.ProcessModePaint)
  630. {
  631. // Drawing Mode
  632. FillPath(brush, path);
  633. if (point.BorderColor != Color.Empty &&
  634. point.BorderWidth > 0 &&
  635. point.BorderDashStyle != ChartDashStyle.NotSet)
  636. {
  637. DrawGraphicsPath(pen, path);
  638. }
  639. }
  640. if (common.ProcessModeRegions)
  641. {
  642. // Check if processing collected data point
  643. if (point.IsCustomPropertySet("_COLLECTED_DATA_POINT"))
  644. {
  645. // Add point to the map area
  646. common.HotRegionsList.AddHotRegion(
  647. (ChartGraphics)this,
  648. path,
  649. false,
  650. point.ReplaceKeywords(point.ToolTip),
  651. string.Empty,
  652. string.Empty,
  653. string.Empty,
  654. point,
  655. ChartElementType.DataPoint);
  656. return;
  657. }
  658. common.HotRegionsList.AddHotRegion(
  659. path,
  660. false,
  661. (ChartGraphics)this,
  662. point,
  663. point.series.Name,
  664. pointIndex);
  665. }
  666. }
  667. }
  668. /// <summary>
  669. /// This method draws projection of 3D pie slice.
  670. /// </summary>
  671. /// <param name="area">Chart area used for drawing</param>
  672. /// <param name="point">Data point which creates this pie slice</param>
  673. /// <param name="brush">Graphic Brush used for drawing</param>
  674. /// <param name="pen">Graphic Pen used for drawing</param>
  675. /// <param name="firstRectPoint">The first point of transformed bounding rectangle</param>
  676. /// <param name="firstPoint">The first arc point of pie slice</param>
  677. /// <param name="secondRectPoint">The second point of transformed bounding rectangle</param>
  678. /// <param name="secondPoint">The second arc point of pie slice</param>
  679. /// <param name="center">The center point of pie slice</param>
  680. /// <param name="startAngle">Start angle of pie slice</param>
  681. /// <param name="sweepAngle">The end angle of pie slice</param>
  682. /// <param name="fill">Fill pie slice with brush</param>
  683. /// <param name="pointIndex"></param>
  684. internal void FillPieSlice( ChartArea area, DataPoint point, SolidBrush brush, Pen pen, PointF firstRectPoint, PointF firstPoint, PointF secondRectPoint, PointF secondPoint, PointF center, float startAngle, float sweepAngle, bool fill, int pointIndex )
  685. {
  686. // Common elements
  687. CommonElements common = area.Common;
  688. // Create a graphics path
  689. using (GraphicsPath path = new GraphicsPath())
  690. {
  691. // It is enough to transform only two points from
  692. // rectangle. This code will create RectangleF from
  693. // top left and bottom right points.
  694. RectangleF pieRectangle = new RectangleF();
  695. pieRectangle.X = firstRectPoint.X;
  696. pieRectangle.Y = firstRectPoint.Y;
  697. pieRectangle.Height = secondRectPoint.Y - firstRectPoint.Y;
  698. pieRectangle.Width = secondRectPoint.X - firstRectPoint.X;
  699. // Angle correction algorithm. After rotation AddArc method should used
  700. // different transformed angles. This method transforms angles.
  701. double angleCorrection = pieRectangle.Height / pieRectangle.Width;
  702. float endAngle;
  703. endAngle = AngleCorrection(startAngle + sweepAngle, angleCorrection);
  704. startAngle = AngleCorrection(startAngle, angleCorrection);
  705. sweepAngle = endAngle - startAngle;
  706. // Add Line between The Center and Arc
  707. path.AddLine(center, firstPoint);
  708. // Add Arc
  709. if (pieRectangle.Height > 0)
  710. {
  711. // If x angle is 0 this arc will be line in projection.
  712. path.AddArc(pieRectangle.X, pieRectangle.Y, pieRectangle.Width, pieRectangle.Height, startAngle, sweepAngle);
  713. }
  714. // Add Line between the end of the arc and the centre.
  715. path.AddLine(secondPoint, center);
  716. if (common.ProcessModePaint)
  717. {
  718. // Get surface colors
  719. Color frontLightColor, leftLightColor, topLightColor, backLightColor, rightLightColor, bottomLightColor;
  720. area.matrix3D.GetLight(brush.Color, out frontLightColor, out backLightColor, out leftLightColor, out rightLightColor, out topLightColor, out bottomLightColor);
  721. Pen newPen = (Pen)pen.Clone();
  722. if (area.Area3DStyle.LightStyle == LightStyle.Realistic && point.BorderColor == Color.Empty)
  723. {
  724. newPen.Color = frontLightColor;
  725. }
  726. // Drawing Mode
  727. if (fill)
  728. {
  729. using (Brush lightBrush = new SolidBrush(frontLightColor))
  730. {
  731. FillPath(lightBrush, path);
  732. }
  733. }
  734. if (point.BorderColor != Color.Empty &&
  735. point.BorderWidth > 0 &&
  736. point.BorderDashStyle != ChartDashStyle.NotSet)
  737. {
  738. DrawGraphicsPath(newPen, path);
  739. }
  740. }
  741. if (common.ProcessModeRegions && fill)
  742. {
  743. // Check if processing collected data point
  744. if (point.IsCustomPropertySet("_COLLECTED_DATA_POINT"))
  745. {
  746. // Add point to the map area
  747. common.HotRegionsList.AddHotRegion(
  748. (ChartGraphics)this,
  749. path,
  750. false,
  751. point.ReplaceKeywords(point.ToolTip),
  752. string.Empty,
  753. string.Empty,
  754. string.Empty,
  755. point,
  756. ChartElementType.DataPoint);
  757. return;
  758. }
  759. common.HotRegionsList.AddHotRegion(path, false, (ChartGraphics)this, point, point.series.Name, pointIndex);
  760. }
  761. }
  762. }
  763. /// <summary>
  764. /// This method draws projection of 3D pie slice.
  765. /// </summary>
  766. /// <param name="area">Chart area used for drawing</param>
  767. /// <param name="point">Data point which creates this Doughnut slice</param>
  768. /// <param name="brush">Graphic Brush used for drawing</param>
  769. /// <param name="pen">Graphic Pen used for drawing</param>
  770. /// <param name="firstRectPoint">The first point of transformed bounding rectangle</param>
  771. /// <param name="firstPoint">The first arc point of Doughnut slice</param>
  772. /// <param name="secondRectPoint">The second point of transformed bounding rectangle</param>
  773. /// <param name="secondPoint">The second arc point of Doughnut slice</param>
  774. /// <param name="threePoint">The three point of Doughnut slice</param>
  775. /// <param name="fourPoint">The four point of Doughnut slice</param>
  776. /// <param name="startAngle">Start angle of Doughnut slice</param>
  777. /// <param name="sweepAngle">The end angle of Doughnut slice</param>
  778. /// <param name="fill">Fill Doughnut slice with brush</param>
  779. /// <param name="doughnutRadius">Radius for doughnut chart</param>
  780. /// <param name="pointIndex">Data Point Index</param>
  781. internal void FillDoughnutSlice( ChartArea area, DataPoint point, SolidBrush brush, Pen pen, PointF firstRectPoint, PointF firstPoint, PointF secondRectPoint, PointF secondPoint, PointF threePoint, PointF fourPoint, float startAngle, float sweepAngle, bool fill, float doughnutRadius, int pointIndex )
  782. {
  783. // Common Elements
  784. CommonElements common = area.Common;
  785. doughnutRadius = 1F - doughnutRadius / 100F;
  786. // Create a graphics path
  787. using (GraphicsPath path = new GraphicsPath())
  788. {
  789. // It is enough to transform only two points from
  790. // rectangle. This code will create RectangleF from
  791. // top left and bottom right points.
  792. RectangleF pieRectangle = new RectangleF();
  793. pieRectangle.X = firstRectPoint.X;
  794. pieRectangle.Y = firstRectPoint.Y;
  795. pieRectangle.Height = secondRectPoint.Y - firstRectPoint.Y;
  796. pieRectangle.Width = secondRectPoint.X - firstRectPoint.X;
  797. RectangleF pieDoughnutRectangle = new RectangleF();
  798. pieDoughnutRectangle.X = pieRectangle.X + pieRectangle.Width * (1F - doughnutRadius) / 2F;
  799. pieDoughnutRectangle.Y = pieRectangle.Y + pieRectangle.Height * (1F - doughnutRadius) / 2F;
  800. pieDoughnutRectangle.Height = pieRectangle.Height * doughnutRadius;
  801. pieDoughnutRectangle.Width = pieRectangle.Width * doughnutRadius;
  802. // Angle correction algorithm. After rotation AddArc method should used
  803. // different transformed angles. This method transforms angles.
  804. double angleCorrection = pieRectangle.Height / pieRectangle.Width;
  805. float endAngle;
  806. endAngle = AngleCorrection(startAngle + sweepAngle, angleCorrection);
  807. startAngle = AngleCorrection(startAngle, angleCorrection);
  808. sweepAngle = endAngle - startAngle;
  809. // Add Line between The Doughnut Arc and Arc
  810. path.AddLine(fourPoint, firstPoint);
  811. // Add Arc
  812. if (pieRectangle.Height > 0)
  813. {
  814. // If x angle is 0 this arc will be line in projection.
  815. path.AddArc(pieRectangle.X, pieRectangle.Y, pieRectangle.Width, pieRectangle.Height, startAngle, sweepAngle);
  816. }
  817. // Add Line between the end of the arc and The Doughnut Arc.
  818. path.AddLine(secondPoint, threePoint);
  819. // Add Doughnut Arc
  820. if (pieDoughnutRectangle.Height > 0)
  821. {
  822. path.AddArc(pieDoughnutRectangle.X, pieDoughnutRectangle.Y, pieDoughnutRectangle.Width, pieDoughnutRectangle.Height, startAngle + sweepAngle, -sweepAngle);
  823. }
  824. if (common.ProcessModePaint)
  825. {
  826. // Get surface colors
  827. Color frontLightColor, leftLightColor, topLightColor, backLightColor, rightLightColor, bottomLightColor;
  828. area.matrix3D.GetLight(brush.Color, out frontLightColor, out backLightColor, out leftLightColor, out rightLightColor, out topLightColor, out bottomLightColor);
  829. Pen newPen = (Pen)pen.Clone();
  830. if (area.Area3DStyle.LightStyle == LightStyle.Realistic && point.BorderColor == Color.Empty)
  831. {
  832. newPen.Color = frontLightColor;
  833. }
  834. // Drawing Mode
  835. if (fill)
  836. {
  837. using (Brush lightBrush = new SolidBrush(frontLightColor))
  838. {
  839. FillPath(lightBrush, path);
  840. }
  841. }
  842. if (point.BorderColor != Color.Empty &&
  843. point.BorderWidth > 0 &&
  844. point.BorderDashStyle != ChartDashStyle.NotSet)
  845. {
  846. DrawGraphicsPath(newPen, path);
  847. }
  848. }
  849. if (common.ProcessModeRegions && fill)
  850. {
  851. // Check if processing collected data point
  852. if (point.IsCustomPropertySet("_COLLECTED_DATA_POINT"))
  853. {
  854. // Add point to the map area
  855. common.HotRegionsList.AddHotRegion(
  856. (ChartGraphics)this,
  857. path,
  858. false,
  859. point.ReplaceKeywords(point.ToolTip),
  860. string.Empty,
  861. string.Empty,
  862. string.Empty,
  863. point,
  864. ChartElementType.DataPoint);
  865. return;
  866. }
  867. // Add points to the map area
  868. common.HotRegionsList.AddHotRegion(
  869. path,
  870. false,
  871. (ChartGraphics)this,
  872. point,
  873. point.series.Name,
  874. pointIndex);
  875. }
  876. }
  877. }
  878. /// <summary>
  879. /// Draw Graphics Path. This method is introduced because of
  880. /// bug in DrawPath method when Pen Width is bigger then 1.
  881. /// </summary>
  882. /// <param name="pen">Pen</param>
  883. /// <param name="path">Graphics Path</param>
  884. private void DrawGraphicsPath( Pen pen, GraphicsPath path )
  885. {
  886. // Normal case. Very fast Drawing.
  887. if( pen.Width < 2 )
  888. {
  889. DrawPath( pen, path );
  890. }
  891. else
  892. {
  893. // Converts each curve in this path into a sequence
  894. // of connected line segments. Slow Drawing.
  895. path.Flatten();
  896. // Set Pen cap
  897. pen.EndCap = LineCap.Round;
  898. pen.StartCap = LineCap.Round;
  899. PointF [] pathPoints;
  900. pathPoints = path.PathPoints;
  901. // Draw any segment as a line.
  902. for( int point = 0; point < path.PathPoints.Length - 1; point++ )
  903. {
  904. PointF [] points;
  905. points = new PointF[2];
  906. points[0] = pathPoints[point];
  907. points[1] = pathPoints[point+1];
  908. DrawLine( pen, points[0], points[1] );
  909. }
  910. }
  911. }
  912. /// <summary>
  913. /// Angle correction algorithm. After rotation different
  914. /// transformed angle should be used. This method transforms angles.
  915. /// </summary>
  916. /// <param name="angle">Not transformed angle</param>
  917. /// <param name="correction">Correction of bounding rectangle (change between width and height)</param>
  918. /// <returns>Transformed angle</returns>
  919. private float AngleCorrection( float angle, double correction )
  920. {
  921. // Make all angles to be between -90 and 90.
  922. if( angle > -90 && angle < 90 )
  923. {
  924. angle = (float)(Math.Atan( Math.Tan( ( angle ) * Math.PI / 180 ) * correction ) * 180 / Math.PI);
  925. }
  926. else if( angle > -270 && angle < -90 )
  927. {
  928. angle = angle + 180;
  929. angle = (float)(Math.Atan( Math.Tan( ( angle ) * Math.PI / 180 ) * correction ) * 180 / Math.PI);
  930. angle = angle - 180;
  931. }
  932. else if( angle > 90 && angle < 270 )
  933. {
  934. angle = angle - 180;
  935. angle = (float)(Math.Atan( Math.Tan( ( angle ) * Math.PI / 180 ) * correction ) * 180 / Math.PI);
  936. angle = angle + 180;
  937. }
  938. else if( angle > 270 && angle < 450 )
  939. {
  940. angle = angle - 360;
  941. angle = (float)(Math.Atan( Math.Tan( ( angle ) * Math.PI / 180 ) * correction ) * 180 / Math.PI);
  942. angle = angle + 360;
  943. }
  944. else if( angle > 450 )
  945. {
  946. angle = angle - 540;
  947. angle = (float)(Math.Atan( Math.Tan( ( angle ) * Math.PI / 180 ) * correction ) * 180 / Math.PI);
  948. angle = angle + 540;
  949. }
  950. return angle;
  951. }
  952. #endregion
  953. #region 3D Surface drawing methods (used in Line charts)
  954. /// <summary>
  955. /// Draws a 3D polygon defined by 4 points in 2D space.
  956. /// </summary>
  957. /// <param name="area">Chart area reference.</param>
  958. /// <param name="matrix">Coordinates transformation matrix.</param>
  959. /// <param name="surfaceName">Name of the surface to draw.</param>
  960. /// <param name="positionZ">Z position of the back side of the 3D surface.</param>
  961. /// <param name="backColor">Color of rectangle</param>
  962. /// <param name="borderColor">Border Color</param>
  963. /// <param name="borderWidth">Border Width</param>
  964. /// <param name="firstPoint">First point.</param>
  965. /// <param name="secondPoint">Second point.</param>
  966. /// <param name="thirdPoint">Third point.</param>
  967. /// <param name="fourthPoint">Fourth point.</param>
  968. /// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
  969. /// <param name="lineSegmentType">AxisName of line segment. Used for step lines and splines.</param>
  970. /// <param name="thinBorders">Thin border will be drawn on specified sides.</param>
  971. /// <returns>Returns elemnt shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
  972. internal GraphicsPath Draw3DPolygon(
  973. ChartArea area,
  974. Matrix3D matrix,
  975. SurfaceNames surfaceName,
  976. float positionZ,
  977. Color backColor,
  978. Color borderColor,
  979. int borderWidth,
  980. DataPoint3D firstPoint,
  981. DataPoint3D secondPoint,
  982. DataPoint3D thirdPoint,
  983. DataPoint3D fourthPoint,
  984. DrawingOperationTypes operationType,
  985. LineSegmentType lineSegmentType,
  986. SurfaceNames thinBorders)
  987. {
  988. // Create graphics path for selection
  989. bool drawElements = ((operationType & DrawingOperationTypes.DrawElement) == DrawingOperationTypes.DrawElement);
  990. GraphicsPath resultPath = ((operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
  991. ? new GraphicsPath() : null;
  992. //**********************************************************************
  993. //** Prepare, transform polygon coordinates
  994. //**********************************************************************
  995. // Define 4 points polygon
  996. Point3D [] points3D = new Point3D[4];
  997. points3D[0] = new Point3D((float)firstPoint.xPosition, (float)firstPoint.yPosition, positionZ);
  998. points3D[1] = new Point3D((float)secondPoint.xPosition, (float)secondPoint.yPosition, positionZ);
  999. points3D[2] = new Point3D((float)thirdPoint.xPosition, (float)thirdPoint.yPosition, positionZ);
  1000. points3D[3] = new Point3D((float)fourthPoint.xPosition, (float)fourthPoint.yPosition, positionZ);
  1001. // Transform coordinates
  1002. matrix.TransformPoints( points3D );
  1003. // Get absolute coordinates and create array of PointF
  1004. PointF[] polygonPoints = new PointF[4];
  1005. polygonPoints[0] = GetAbsolutePoint(points3D[0].PointF);
  1006. polygonPoints[1] = GetAbsolutePoint(points3D[1].PointF);
  1007. polygonPoints[2] = GetAbsolutePoint(points3D[2].PointF);
  1008. polygonPoints[3] = GetAbsolutePoint(points3D[3].PointF);
  1009. //**********************************************************************
  1010. //** Define drawing colors
  1011. //**********************************************************************
  1012. bool topIsVisible = IsSurfaceVisible( points3D[0], points3D[1], points3D[2]);
  1013. Color polygonColor = matrix.GetPolygonLight( points3D, backColor, topIsVisible, area.Area3DStyle.Rotation, surfaceName, area.ReverseSeriesOrder );
  1014. Color surfaceBorderColor = borderColor;
  1015. if(surfaceBorderColor == Color.Empty)
  1016. {
  1017. // If border color is emty use color slightly darker than main back color
  1018. surfaceBorderColor = ChartGraphics.GetGradientColor( backColor, Color.Black, 0.2 );
  1019. }
  1020. //**********************************************************************
  1021. //** Draw elements if required.
  1022. //**********************************************************************
  1023. Pen thickBorderPen = null;
  1024. if(drawElements)
  1025. {
  1026. // Remember SmoothingMode and turn off anti aliasing
  1027. SmoothingMode oldSmoothingMode = SmoothingMode;
  1028. SmoothingMode = SmoothingMode.Default;
  1029. // Draw the polygon
  1030. using (Brush brush = new SolidBrush(polygonColor))
  1031. {
  1032. FillPolygon(brush, polygonPoints);
  1033. }
  1034. // Return old smoothing mode
  1035. SmoothingMode = oldSmoothingMode;
  1036. // Draw thin polygon border of darker color around the whole polygon
  1037. if(thinBorders != 0)
  1038. {
  1039. Pen thinLinePen = new Pen(surfaceBorderColor, 1);
  1040. if( (thinBorders & SurfaceNames.Left) != 0 )
  1041. DrawLine(thinLinePen, polygonPoints[3], polygonPoints[0]);
  1042. if( (thinBorders & SurfaceNames.Right) != 0 )
  1043. DrawLine(thinLinePen, polygonPoints[1], polygonPoints[2]);
  1044. if( (thinBorders & SurfaceNames.Top) != 0 )
  1045. DrawLine(thinLinePen, polygonPoints[0], polygonPoints[1]);
  1046. if( (thinBorders & SurfaceNames.Bottom) != 0 )
  1047. DrawLine(thinLinePen, polygonPoints[2], polygonPoints[3]);
  1048. }
  1049. else if(polygonColor.A == 255)
  1050. {
  1051. DrawPolygon(new Pen(polygonColor, 1), polygonPoints);
  1052. }
  1053. // Create thick border line pen
  1054. thickBorderPen = new Pen(surfaceBorderColor, borderWidth);
  1055. thickBorderPen.StartCap = LineCap.Round;
  1056. thickBorderPen.EndCap = LineCap.Round;
  1057. // Draw thick Top & Bottom lines
  1058. DrawLine(thickBorderPen, polygonPoints[0], polygonPoints[1]);
  1059. DrawLine(thickBorderPen, polygonPoints[2], polygonPoints[3]);
  1060. // Draw thick Right & Left lines on first & last segments of the line
  1061. if(lineSegmentType == LineSegmentType.First)
  1062. {
  1063. DrawLine(thickBorderPen, polygonPoints[3], polygonPoints[0]);
  1064. }
  1065. else if(lineSegmentType == LineSegmentType.Last)
  1066. {
  1067. DrawLine(thickBorderPen, polygonPoints[1], polygonPoints[2]);
  1068. }
  1069. }
  1070. //**********************************************************************
  1071. //** Redraw front line of the previuos line segment.
  1072. //**********************************************************************
  1073. if(area.Area3DStyle.Perspective == 0)
  1074. {
  1075. if(frontLinePoint1 != PointF.Empty && frontLinePen != null)
  1076. {
  1077. if( (frontLinePoint1.X == polygonPoints[0].X &&
  1078. frontLinePoint1.Y == polygonPoints[0].Y ||
  1079. frontLinePoint2.X == polygonPoints[1].X &&
  1080. frontLinePoint2.Y == polygonPoints[1].Y ) ||
  1081. (frontLinePoint1.X == polygonPoints[1].X &&
  1082. frontLinePoint1.Y == polygonPoints[1].Y ||
  1083. frontLinePoint2.X == polygonPoints[0].X &&
  1084. frontLinePoint2.Y == polygonPoints[0].Y ) ||
  1085. (frontLinePoint1.X == polygonPoints[3].X &&
  1086. frontLinePoint1.Y == polygonPoints[3].Y ||
  1087. frontLinePoint2.X == polygonPoints[2].X &&
  1088. frontLinePoint2.Y == polygonPoints[2].Y) ||
  1089. (frontLinePoint1.X == polygonPoints[2].X &&
  1090. frontLinePoint1.Y == polygonPoints[2].Y ||
  1091. frontLinePoint2.X == polygonPoints[3].X &&
  1092. frontLinePoint2.Y == polygonPoints[3].Y) )
  1093. {
  1094. // Do not draw the line if it will be overlapped with current
  1095. }
  1096. else
  1097. {
  1098. // Draw line !!!!
  1099. DrawLine(
  1100. frontLinePen,
  1101. (float)Math.Round(frontLinePoint1.X),
  1102. (float)Math.Round(frontLinePoint1.Y),
  1103. (float)Math.Round(frontLinePoint2.X),
  1104. (float)Math.Round(frontLinePoint2.Y) );
  1105. }
  1106. // Reset line properties
  1107. frontLinePen = null;
  1108. frontLinePoint1 = PointF.Empty;
  1109. frontLinePoint2 = PointF.Empty;
  1110. }
  1111. //**********************************************************************
  1112. //** Check if front line should be redrawn whith the next segment.
  1113. //**********************************************************************
  1114. if(drawElements)
  1115. {
  1116. // Add top line
  1117. frontLinePen = thickBorderPen;
  1118. frontLinePoint1 = polygonPoints[0];
  1119. frontLinePoint2 = polygonPoints[1];
  1120. }
  1121. }
  1122. // Calculate path for selection
  1123. if(resultPath != null)
  1124. {
  1125. // Add polygon to the path
  1126. resultPath.AddPolygon(polygonPoints);
  1127. }
  1128. return resultPath;
  1129. }
  1130. /// <summary>
  1131. /// Helper method which returns the splines flatten path.
  1132. /// </summary>
  1133. /// <param name="area">Chart area reference.</param>
  1134. /// <param name="positionZ">Z position of the back side of the 3D surface.</param>
  1135. /// <param name="firstPoint">First point.</param>
  1136. /// <param name="secondPoint">Second point.</param>
  1137. /// <param name="points">Array of points.</param>
  1138. /// <param name="tension">Line tension.</param>
  1139. /// <param name="flatten">Flatten result path.</param>
  1140. /// <param name="translateCoordinates">Indicates that points coordinates should be translated.</param>
  1141. /// <param name="yValueIndex">Index of the Y value to use.</param>
  1142. /// <returns>Spline path.</returns>
  1143. internal GraphicsPath GetSplineFlattenPath(
  1144. ChartArea area,
  1145. float positionZ,
  1146. DataPoint3D firstPoint,
  1147. DataPoint3D secondPoint,
  1148. ArrayList points,
  1149. float tension,
  1150. bool flatten,
  1151. bool translateCoordinates,
  1152. int yValueIndex)
  1153. {
  1154. // Find first spline point index
  1155. int firtsSplinePointIndex = (firstPoint.index < secondPoint.index) ? firstPoint.index : secondPoint.index;
  1156. --firtsSplinePointIndex;
  1157. if(firtsSplinePointIndex >= (points.Count - 2) )
  1158. {
  1159. --firtsSplinePointIndex;
  1160. }
  1161. if(firtsSplinePointIndex < 1)
  1162. {
  1163. firtsSplinePointIndex = 1;
  1164. }
  1165. // Find four points which are required to draw the spline
  1166. int pointArrayIndex = int.MinValue;
  1167. DataPoint3D [] splineDataPoints = new DataPoint3D[4];
  1168. splineDataPoints[0] = FindPointByIndex(points, firtsSplinePointIndex, null, ref pointArrayIndex);
  1169. splineDataPoints[1] = FindPointByIndex(points, firtsSplinePointIndex + 1, null, ref pointArrayIndex);
  1170. splineDataPoints[2] = FindPointByIndex(points, firtsSplinePointIndex + 2, null, ref pointArrayIndex);
  1171. splineDataPoints[3] = FindPointByIndex(points, firtsSplinePointIndex + 3, null, ref pointArrayIndex);
  1172. // Get offset of spline segment in array
  1173. int splineSegmentOffset = 0;
  1174. while(splineSegmentOffset < 4)
  1175. {
  1176. if(splineDataPoints[splineSegmentOffset].index == firstPoint.index ||
  1177. splineDataPoints[splineSegmentOffset].index == secondPoint.index)
  1178. {
  1179. break;
  1180. }
  1181. ++splineSegmentOffset;
  1182. }
  1183. // Get number of found points
  1184. int nonNullPoints = 2;
  1185. if(splineDataPoints[2] != null)
  1186. ++nonNullPoints;
  1187. if(splineDataPoints[3] != null)
  1188. ++nonNullPoints;
  1189. // Get coordinates and create array of PointF for the front spline
  1190. PointF[] polygonPointsFront = new PointF[nonNullPoints];
  1191. if(yValueIndex == 0)
  1192. {
  1193. polygonPointsFront[0] = new PointF((float)splineDataPoints[0].xPosition, (float)splineDataPoints[0].yPosition);
  1194. polygonPointsFront[1] = new PointF((float)splineDataPoints[1].xPosition, (float)splineDataPoints[1].yPosition);
  1195. if(nonNullPoints > 2)
  1196. polygonPointsFront[2] = new PointF((float)splineDataPoints[2].xPosition, (float)splineDataPoints[2].yPosition);
  1197. if(nonNullPoints > 3)
  1198. polygonPointsFront[3] = new PointF((float)splineDataPoints[3].xPosition, (float)splineDataPoints[3].yPosition);
  1199. }
  1200. else
  1201. {
  1202. // Set active vertical axis
  1203. Axis vAxis = (firstPoint.dataPoint.series.YAxisType == AxisType.Primary) ? area.AxisY : area.AxisY2;
  1204. float secondYValue = (float)vAxis.GetPosition(splineDataPoints[0].dataPoint.YValues[yValueIndex]);
  1205. polygonPointsFront[0] = new PointF((float)splineDataPoints[0].xPosition, secondYValue);
  1206. secondYValue = (float)vAxis.GetPosition(splineDataPoints[1].dataPoint.YValues[yValueIndex]);
  1207. polygonPointsFront[1] = new PointF((float)splineDataPoints[1].xPosition, secondYValue);
  1208. if(nonNullPoints > 2)
  1209. {
  1210. secondYValue = (float)vAxis.GetPosition(splineDataPoints[2].dataPoint.YValues[yValueIndex]);
  1211. polygonPointsFront[2] = new PointF((float)splineDataPoints[2].xPosition, secondYValue);
  1212. }
  1213. if(nonNullPoints > 3)
  1214. {
  1215. secondYValue = (float)vAxis.GetPosition(splineDataPoints[3].dataPoint.YValues[yValueIndex]);
  1216. polygonPointsFront[3] = new PointF((float)splineDataPoints[3].xPosition, secondYValue);
  1217. }
  1218. }
  1219. // Translate points coordinates in 3D space and get absolute coordinate
  1220. if(translateCoordinates)
  1221. {
  1222. // Prepare array of points
  1223. Point3D[] points3D = new Point3D[nonNullPoints];
  1224. for(int index = 0; index < nonNullPoints; index++)
  1225. {
  1226. points3D[index] = new Point3D(polygonPointsFront[index].X, polygonPointsFront[index].Y, positionZ);
  1227. }
  1228. // Make coordinates transformation
  1229. area.matrix3D.TransformPoints( points3D );
  1230. // Get absolute values
  1231. for(int index = 0; index < nonNullPoints; index++)
  1232. {
  1233. polygonPointsFront[index] = GetAbsolutePoint(points3D[index].PointF);
  1234. }
  1235. }
  1236. // Create graphics path for the front spline surface and flatten it.
  1237. GraphicsPath splineSurfacePath = new GraphicsPath();
  1238. splineSurfacePath.AddCurve(polygonPointsFront, splineSegmentOffset, 1, tension);
  1239. if(flatten)
  1240. {
  1241. splineSurfacePath.Flatten();
  1242. }
  1243. // IsReversed points order
  1244. if(firstPoint.index > secondPoint.index)
  1245. {
  1246. splineSurfacePath.Reverse();
  1247. }
  1248. return splineSurfacePath;
  1249. }
  1250. /// <summary>
  1251. /// Draws a 3D spline surface connecting the two specified points in 2D space.
  1252. /// Used to draw Spline based charts.
  1253. /// </summary>
  1254. /// <param name="area">Chart area reference.</param>
  1255. /// <param name="matrix">Coordinates transformation matrix.</param>
  1256. /// <param name="lightStyle">LightStyle style (None, Simplistic, Realistic).</param>
  1257. /// <param name="surfaceName">Name of the surface to draw.</param>
  1258. /// <param name="positionZ">Z position of the back side of the 3D surface.</param>
  1259. /// <param name="depth">Depth of the 3D surface.</param>
  1260. /// <param name="backColor">Color of rectangle</param>
  1261. /// <param name="borderColor">Border Color</param>
  1262. /// <param name="borderWidth">Border Width</param>
  1263. /// <param name="borderDashStyle">Border Style</param>
  1264. /// <param name="firstPoint">First point.</param>
  1265. /// <param name="secondPoint">Second point.</param>
  1266. /// <param name="points">Array of points.</param>
  1267. /// <param name="pointIndex">Index of point to draw.</param>
  1268. /// <param name="tension">Line tension.</param>
  1269. /// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
  1270. /// <param name="forceThinBorder">Thin border will be drawn on all segments.</param>
  1271. /// <param name="forceThickBorder">Thick border will be drawn on all segments.</param>
  1272. /// <param name="reversedSeriesOrder">Series are drawn in reversed order.</param>
  1273. /// <param name="multiSeries">Multiple series are drawn at the same time.</param>
  1274. /// <param name="yValueIndex">Index of the Y value to use.</param>
  1275. /// <param name="clipInsideArea">Surface should be clipped inside plotting area.</param>
  1276. /// <returns>Returns elemnt shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
  1277. internal GraphicsPath Draw3DSplineSurface(
  1278. ChartArea area,
  1279. Matrix3D matrix,
  1280. LightStyle lightStyle,
  1281. SurfaceNames surfaceName,
  1282. float positionZ,
  1283. float depth,
  1284. Color backColor,
  1285. Color borderColor,
  1286. int borderWidth,
  1287. ChartDashStyle borderDashStyle,
  1288. DataPoint3D firstPoint,
  1289. DataPoint3D secondPoint,
  1290. ArrayList points,
  1291. int pointIndex,
  1292. float tension,
  1293. DrawingOperationTypes operationType,
  1294. bool forceThinBorder,
  1295. bool forceThickBorder,
  1296. bool reversedSeriesOrder,
  1297. bool multiSeries,
  1298. int yValueIndex,
  1299. bool clipInsideArea)
  1300. {
  1301. // If zero tension is specified - draw a Line Surface
  1302. if(tension == 0f)
  1303. {
  1304. return Draw3DSurface(
  1305. area,
  1306. matrix,
  1307. lightStyle,
  1308. surfaceName,
  1309. positionZ,
  1310. depth,
  1311. backColor,
  1312. borderColor,
  1313. borderWidth,
  1314. borderDashStyle,
  1315. firstPoint,
  1316. secondPoint,
  1317. points,
  1318. pointIndex,
  1319. tension,
  1320. operationType,
  1321. LineSegmentType.Single,
  1322. forceThinBorder,
  1323. forceThickBorder,
  1324. reversedSeriesOrder,
  1325. multiSeries,
  1326. yValueIndex,
  1327. clipInsideArea);
  1328. }
  1329. // Create graphics path for selection
  1330. GraphicsPath resultPath = ((operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
  1331. ? new GraphicsPath() : null;
  1332. // Get spline flatten path
  1333. GraphicsPath splineSurfacePath = GetSplineFlattenPath(
  1334. area, positionZ,
  1335. firstPoint, secondPoint, points, tension, true, false, yValueIndex);
  1336. // Check if reversed drawing order required
  1337. bool reversed = false;
  1338. if((pointIndex + 1) < points.Count)
  1339. {
  1340. DataPoint3D p = (DataPoint3D)points[pointIndex + 1];
  1341. if(p.index == firstPoint.index)
  1342. {
  1343. reversed = true;
  1344. }
  1345. }
  1346. if(reversed)
  1347. {
  1348. splineSurfacePath.Reverse();
  1349. }
  1350. // Loop through all segment lines the spline consists off
  1351. PointF[] splinePathPoints = splineSurfacePath.PathPoints;
  1352. DataPoint3D dp1 = new DataPoint3D();
  1353. DataPoint3D dp2 = new DataPoint3D();
  1354. LineSegmentType lineSegmentType = LineSegmentType.Middle;
  1355. for(int pIndex = 1; pIndex < splinePathPoints.Length; pIndex++)
  1356. {
  1357. bool forceSegmentThinBorder = false;
  1358. bool forceSegmentThickBorder = false;
  1359. // Calculate surface coordinates
  1360. if(!reversed)
  1361. {
  1362. dp1.index = firstPoint.index;
  1363. dp1.dataPoint = firstPoint.dataPoint;
  1364. dp1.xPosition = splinePathPoints[pIndex - 1].X;
  1365. dp1.yPosition = splinePathPoints[pIndex - 1].Y;
  1366. dp2.index = secondPoint.index;
  1367. dp2.index = secondPoint.index;
  1368. dp2.xPosition = splinePathPoints[pIndex].X;
  1369. dp2.yPosition = splinePathPoints[pIndex].Y;
  1370. }
  1371. else
  1372. {
  1373. dp2.index = firstPoint.index;
  1374. dp2.dataPoint = firstPoint.dataPoint;
  1375. dp2.xPosition = splinePathPoints[pIndex - 1].X;
  1376. dp2.yPosition = splinePathPoints[pIndex - 1].Y;
  1377. dp1.index = secondPoint.index;
  1378. dp1.dataPoint = secondPoint.dataPoint;
  1379. dp1.xPosition = splinePathPoints[pIndex].X;
  1380. dp1.yPosition = splinePathPoints[pIndex].Y;
  1381. }
  1382. // Get sefment type
  1383. lineSegmentType = LineSegmentType.Middle;
  1384. if(pIndex == 1)
  1385. {
  1386. if(!reversed)
  1387. lineSegmentType = LineSegmentType.First;
  1388. else
  1389. lineSegmentType = LineSegmentType.Last;
  1390. forceSegmentThinBorder = forceThinBorder;
  1391. forceSegmentThickBorder = forceThickBorder;
  1392. }
  1393. else if(pIndex == splinePathPoints.Length - 1)
  1394. {
  1395. if(!reversed)
  1396. lineSegmentType = LineSegmentType.Last;
  1397. else
  1398. lineSegmentType = LineSegmentType.First;
  1399. forceSegmentThinBorder = forceThinBorder;
  1400. forceSegmentThickBorder = forceThickBorder;
  1401. }
  1402. // Draw flat surface
  1403. GraphicsPath segmentResultPath = Draw3DSurface(
  1404. area,
  1405. matrix,
  1406. lightStyle,
  1407. surfaceName,
  1408. positionZ,
  1409. depth,
  1410. backColor,
  1411. borderColor,
  1412. borderWidth,
  1413. borderDashStyle,
  1414. dp1,
  1415. dp2,
  1416. points,
  1417. pointIndex,
  1418. 0f,
  1419. operationType,
  1420. lineSegmentType,
  1421. forceSegmentThinBorder,
  1422. forceSegmentThickBorder,
  1423. reversedSeriesOrder,
  1424. multiSeries,
  1425. yValueIndex,
  1426. clipInsideArea);
  1427. // Add selection path
  1428. if(resultPath != null && segmentResultPath != null && segmentResultPath.PointCount > 0)
  1429. {
  1430. resultPath.AddPath(segmentResultPath, true);
  1431. }
  1432. }
  1433. return resultPath;
  1434. }
  1435. /// <summary>
  1436. /// Draws a 3D surface connecting the two specified points in 2D space.
  1437. /// Used to draw Line based charts.
  1438. /// </summary>
  1439. /// <param name="area">Chart area reference.</param>
  1440. /// <param name="matrix">Coordinates transformation matrix.</param>
  1441. /// <param name="lightStyle">LightStyle style (None, Simplistic, Realistic).</param>
  1442. /// <param name="surfaceName">Name of the surface to draw.</param>
  1443. /// <param name="positionZ">Z position of the back side of the 3D surface.</param>
  1444. /// <param name="depth">Depth of the 3D surface.</param>
  1445. /// <param name="backColor">Color of rectangle</param>
  1446. /// <param name="borderColor">Border Color</param>
  1447. /// <param name="borderWidth">Border Width</param>
  1448. /// <param name="borderDashStyle">Border Style</param>
  1449. /// <param name="firstPoint">First point.</param>
  1450. /// <param name="secondPoint">Second point.</param>
  1451. /// <param name="points">Array of points.</param>
  1452. /// <param name="pointIndex">Index of point to draw.</param>
  1453. /// <param name="tension">Line tension.</param>
  1454. /// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
  1455. /// <param name="lineSegmentType">AxisName of line segment. Used for step lines and splines.</param>
  1456. /// <param name="forceThinBorder">Thin border will be drawn on all segments.</param>
  1457. /// <param name="forceThickBorder">Thick border will be drawn on all segments.</param>
  1458. /// <param name="reversedSeriesOrder">Series are drawn in reversed order.</param>
  1459. /// <param name="multiSeries">Multiple series are drawn at the same time.</param>
  1460. /// <param name="yValueIndex">Index of the Y value to use.</param>
  1461. /// <param name="clipInsideArea">Surface should be clipped inside plotting area.</param>
  1462. /// <returns>Returns elemnt shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
  1463. internal GraphicsPath Draw3DSurface(
  1464. ChartArea area,
  1465. Matrix3D matrix,
  1466. LightStyle lightStyle,
  1467. SurfaceNames surfaceName,
  1468. float positionZ,
  1469. float depth,
  1470. Color backColor,
  1471. Color borderColor,
  1472. int borderWidth,
  1473. ChartDashStyle borderDashStyle,
  1474. DataPoint3D firstPoint,
  1475. DataPoint3D secondPoint,
  1476. ArrayList points,
  1477. int pointIndex,
  1478. float tension,
  1479. DrawingOperationTypes operationType,
  1480. LineSegmentType lineSegmentType,
  1481. bool forceThinBorder,
  1482. bool forceThickBorder,
  1483. bool reversedSeriesOrder,
  1484. bool multiSeries,
  1485. int yValueIndex,
  1486. bool clipInsideArea)
  1487. {
  1488. // If non-zero tension is specified - draw a Spline Surface
  1489. if(tension != 0f)
  1490. {
  1491. return Draw3DSplineSurface(
  1492. area,
  1493. matrix,
  1494. lightStyle,
  1495. surfaceName,
  1496. positionZ,
  1497. depth,
  1498. backColor,
  1499. borderColor,
  1500. borderWidth,
  1501. borderDashStyle,
  1502. firstPoint,
  1503. secondPoint,
  1504. points,
  1505. pointIndex,
  1506. tension,
  1507. operationType,
  1508. forceThinBorder,
  1509. forceThickBorder,
  1510. reversedSeriesOrder,
  1511. multiSeries,
  1512. yValueIndex,
  1513. clipInsideArea);
  1514. }
  1515. //**********************************************************************
  1516. //** Create graphics path for selection
  1517. //**********************************************************************
  1518. bool drawElements = ((operationType & DrawingOperationTypes.DrawElement) == DrawingOperationTypes.DrawElement);
  1519. GraphicsPath resultPath = ((operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
  1520. ? new GraphicsPath() : null;
  1521. //**********************************************************************
  1522. //** Check surface coordinates
  1523. //**********************************************************************
  1524. if((decimal)firstPoint.xPosition == (decimal)secondPoint.xPosition &&
  1525. (decimal)firstPoint.yPosition == (decimal)secondPoint.yPosition)
  1526. {
  1527. return resultPath;
  1528. }
  1529. //**********************************************************************
  1530. //** Clip surface
  1531. //**********************************************************************
  1532. // Check if line between the first and second points intersects with
  1533. // plotting area top or bottom boundary
  1534. if(clipInsideArea)
  1535. {
  1536. //****************************************************************
  1537. //** Round plot are position and point coordinates
  1538. //****************************************************************
  1539. int decimals = 3;
  1540. decimal plotAreaPositionX = Math.Round((decimal)area.PlotAreaPosition.X, decimals);
  1541. decimal plotAreaPositionY = Math.Round((decimal)area.PlotAreaPosition.Y, decimals);
  1542. decimal plotAreaPositionRight = Math.Round((decimal)area.PlotAreaPosition.Right, decimals);
  1543. decimal plotAreaPositionBottom = Math.Round((decimal)area.PlotAreaPosition.Bottom, decimals);
  1544. // Make area a little bit bigger
  1545. plotAreaPositionX -= 0.001M;
  1546. plotAreaPositionY -= 0.001M;
  1547. plotAreaPositionRight += 0.001M;
  1548. plotAreaPositionBottom += 0.001M;
  1549. // Chech data points X values
  1550. if((decimal)firstPoint.xPosition < plotAreaPositionX ||
  1551. (decimal)firstPoint.xPosition > plotAreaPositionRight ||
  1552. (decimal)secondPoint.xPosition < plotAreaPositionX ||
  1553. (decimal)secondPoint.xPosition > plotAreaPositionRight )
  1554. {
  1555. // Check if surface completly out of the plot area
  1556. if((decimal)firstPoint.xPosition < plotAreaPositionX &&
  1557. (decimal)secondPoint.xPosition < plotAreaPositionX)
  1558. {
  1559. return resultPath;
  1560. }
  1561. // Check if surface completly out of the plot area
  1562. if((decimal)firstPoint.xPosition > plotAreaPositionRight &&
  1563. (decimal)secondPoint.xPosition > plotAreaPositionRight)
  1564. {
  1565. return resultPath;
  1566. }
  1567. // Only part of the surface is outside - fix X value and adjust Y value
  1568. if((decimal)firstPoint.xPosition < plotAreaPositionX)
  1569. {
  1570. firstPoint.yPosition = ((double)plotAreaPositionX - secondPoint.xPosition) /
  1571. (firstPoint.xPosition - secondPoint.xPosition) *
  1572. (firstPoint.yPosition - secondPoint.yPosition) +
  1573. secondPoint.yPosition;
  1574. firstPoint.xPosition = (double)plotAreaPositionX;
  1575. }
  1576. else if((decimal)firstPoint.xPosition > plotAreaPositionRight)
  1577. {
  1578. firstPoint.yPosition = ((double)plotAreaPositionRight - secondPoint.xPosition) /
  1579. (firstPoint.xPosition - secondPoint.xPosition) *
  1580. (firstPoint.yPosition - secondPoint.yPosition) +
  1581. secondPoint.yPosition;
  1582. firstPoint.xPosition = (double)plotAreaPositionRight;
  1583. }
  1584. if((decimal)secondPoint.xPosition < plotAreaPositionX)
  1585. {
  1586. secondPoint.yPosition = ((double)plotAreaPositionX - secondPoint.xPosition) /
  1587. (firstPoint.xPosition - secondPoint.xPosition) *
  1588. (firstPoint.yPosition - secondPoint.yPosition) +
  1589. secondPoint.yPosition;
  1590. secondPoint.xPosition = (double)plotAreaPositionX;
  1591. }
  1592. else if((decimal)secondPoint.xPosition > plotAreaPositionRight)
  1593. {
  1594. secondPoint.yPosition = ((double)plotAreaPositionRight - secondPoint.xPosition) /
  1595. (firstPoint.xPosition - secondPoint.xPosition) *
  1596. (firstPoint.yPosition - secondPoint.yPosition) +
  1597. secondPoint.yPosition;
  1598. secondPoint.xPosition = (double)plotAreaPositionRight;
  1599. }
  1600. }
  1601. // Chech data points Y values
  1602. if((decimal)firstPoint.yPosition < plotAreaPositionY ||
  1603. (decimal)firstPoint.yPosition > plotAreaPositionBottom ||
  1604. (decimal)secondPoint.yPosition < plotAreaPositionY ||
  1605. (decimal)secondPoint.yPosition > plotAreaPositionBottom )
  1606. {
  1607. // Remember previous y positions
  1608. double prevFirstPointY = firstPoint.yPosition;
  1609. double prevSecondPointY = secondPoint.yPosition;
  1610. // Check if whole line is outside plotting region
  1611. bool surfaceCompletlyOutside = false;
  1612. if((decimal)firstPoint.yPosition < plotAreaPositionY &&
  1613. (decimal)secondPoint.yPosition < plotAreaPositionY)
  1614. {
  1615. surfaceCompletlyOutside = true;
  1616. firstPoint.yPosition = (double)plotAreaPositionY;
  1617. secondPoint.yPosition = (double)plotAreaPositionY;
  1618. }
  1619. if((decimal)firstPoint.yPosition > plotAreaPositionBottom &&
  1620. (decimal)secondPoint.yPosition > plotAreaPositionBottom)
  1621. {
  1622. surfaceCompletlyOutside = true;
  1623. firstPoint.yPosition = (double)plotAreaPositionBottom;
  1624. secondPoint.yPosition = (double)plotAreaPositionBottom;
  1625. }
  1626. // Calculate color used to draw "cut" surfaces
  1627. Color cutSurfaceBackColor = ChartGraphics.GetGradientColor(backColor, Color.Black, 0.5);
  1628. Color cutSurfaceBorderColor = ChartGraphics.GetGradientColor(borderColor, Color.Black, 0.5);
  1629. // Draw just one surface
  1630. if(surfaceCompletlyOutside)
  1631. {
  1632. resultPath = this.Draw3DSurface(
  1633. area, matrix, lightStyle, surfaceName, positionZ, depth,
  1634. cutSurfaceBackColor, cutSurfaceBorderColor, borderWidth, borderDashStyle,
  1635. firstPoint, secondPoint,
  1636. points, pointIndex, tension, operationType, lineSegmentType,
  1637. forceThinBorder, forceThickBorder, reversedSeriesOrder,
  1638. multiSeries, yValueIndex, clipInsideArea);
  1639. // Restore previous y positions
  1640. firstPoint.yPosition = prevFirstPointY;
  1641. secondPoint.yPosition = prevSecondPointY;
  1642. return resultPath;
  1643. }
  1644. // Get intersection point
  1645. DataPoint3D intersectionPoint = new DataPoint3D();
  1646. intersectionPoint.yPosition = (double)plotAreaPositionY;
  1647. if((decimal)firstPoint.yPosition > plotAreaPositionBottom ||
  1648. (decimal)secondPoint.yPosition > plotAreaPositionBottom )
  1649. {
  1650. intersectionPoint.yPosition = (double)plotAreaPositionBottom;
  1651. }
  1652. intersectionPoint.xPosition = (intersectionPoint.yPosition - secondPoint.yPosition) *
  1653. (firstPoint.xPosition - secondPoint.xPosition) /
  1654. (firstPoint.yPosition - secondPoint.yPosition) +
  1655. secondPoint.xPosition;
  1656. // Check if there are 2 intersection points (3 segments)
  1657. int segmentNumber = 2;
  1658. DataPoint3D intersectionPoint2 = null;
  1659. if( ((decimal)firstPoint.yPosition < plotAreaPositionY &&
  1660. (decimal)secondPoint.yPosition > plotAreaPositionBottom) ||
  1661. ((decimal)firstPoint.yPosition > plotAreaPositionBottom &&
  1662. (decimal)secondPoint.yPosition < plotAreaPositionY))
  1663. {
  1664. segmentNumber = 3;
  1665. intersectionPoint2 = new DataPoint3D();
  1666. if((decimal)intersectionPoint.yPosition == plotAreaPositionY)
  1667. {
  1668. intersectionPoint2.yPosition = (double)plotAreaPositionBottom;
  1669. }
  1670. else
  1671. {
  1672. intersectionPoint2.yPosition = (double)plotAreaPositionY;
  1673. }
  1674. intersectionPoint2.xPosition = (intersectionPoint2.yPosition - secondPoint.yPosition) *
  1675. (firstPoint.xPosition - secondPoint.xPosition) /
  1676. (firstPoint.yPosition - secondPoint.yPosition) +
  1677. secondPoint.xPosition;
  1678. // Switch intersection points
  1679. if((decimal)firstPoint.yPosition > plotAreaPositionBottom)
  1680. {
  1681. DataPoint3D tempPoint = new DataPoint3D();
  1682. tempPoint.xPosition = intersectionPoint.xPosition;
  1683. tempPoint.yPosition = intersectionPoint.yPosition;
  1684. intersectionPoint.xPosition = intersectionPoint2.xPosition;
  1685. intersectionPoint.yPosition = intersectionPoint2.yPosition;
  1686. intersectionPoint2.xPosition = tempPoint.xPosition;
  1687. intersectionPoint2.yPosition = tempPoint.yPosition;
  1688. }
  1689. }
  1690. // Adjust points Y values
  1691. bool firstSegmentVisible = true;
  1692. if((decimal)firstPoint.yPosition < plotAreaPositionY)
  1693. {
  1694. firstSegmentVisible = false;
  1695. firstPoint.yPosition = (double)plotAreaPositionY;
  1696. }
  1697. else if((decimal)firstPoint.yPosition > plotAreaPositionBottom)
  1698. {
  1699. firstSegmentVisible = false;
  1700. firstPoint.yPosition = (double)plotAreaPositionBottom;
  1701. }
  1702. if((decimal)secondPoint.yPosition < plotAreaPositionY)
  1703. {
  1704. secondPoint.yPosition = (double)plotAreaPositionY;
  1705. }
  1706. else if((decimal)secondPoint.yPosition > plotAreaPositionBottom)
  1707. {
  1708. secondPoint.yPosition = (double)plotAreaPositionBottom;
  1709. }
  1710. // Check if reversed drawing order required
  1711. bool reversed = false;
  1712. if((pointIndex + 1) < points.Count)
  1713. {
  1714. DataPoint3D p = (DataPoint3D)points[pointIndex + 1];
  1715. if(p.index == firstPoint.index)
  1716. {
  1717. reversed = true;
  1718. }
  1719. }
  1720. // Draw surfaces in 2 or 3 segments
  1721. for(int segmentIndex = 0; segmentIndex < 3; segmentIndex++)
  1722. {
  1723. GraphicsPath segmentPath = null;
  1724. if(segmentIndex == 0 && !reversed ||
  1725. segmentIndex == 2 && reversed)
  1726. {
  1727. // Draw first segment
  1728. if(intersectionPoint2 == null)
  1729. {
  1730. intersectionPoint2 = intersectionPoint;
  1731. }
  1732. intersectionPoint2.dataPoint = secondPoint.dataPoint;
  1733. intersectionPoint2.index = secondPoint.index;
  1734. segmentPath = this.Draw3DSurface(
  1735. area, matrix, lightStyle, surfaceName, positionZ, depth,
  1736. (firstSegmentVisible && segmentNumber != 3) ? backColor : cutSurfaceBackColor,
  1737. (firstSegmentVisible && segmentNumber != 3) ? borderColor : cutSurfaceBorderColor,
  1738. borderWidth, borderDashStyle,
  1739. firstPoint, intersectionPoint2,
  1740. points, pointIndex, tension, operationType, lineSegmentType,
  1741. forceThinBorder, forceThickBorder, reversedSeriesOrder,
  1742. multiSeries, yValueIndex, clipInsideArea);
  1743. }
  1744. if(segmentIndex == 1 && intersectionPoint2 != null && segmentNumber == 3)
  1745. {
  1746. // Draw middle segment
  1747. intersectionPoint2.dataPoint = secondPoint.dataPoint;
  1748. intersectionPoint2.index = secondPoint.index;
  1749. segmentPath = this.Draw3DSurface(
  1750. area, matrix, lightStyle, surfaceName, positionZ, depth,
  1751. backColor,
  1752. borderColor,
  1753. borderWidth, borderDashStyle,
  1754. intersectionPoint, intersectionPoint2,
  1755. points, pointIndex, tension, operationType, lineSegmentType,
  1756. forceThinBorder, forceThickBorder, reversedSeriesOrder,
  1757. multiSeries, yValueIndex, clipInsideArea);
  1758. }
  1759. if(segmentIndex == 2 && !reversed ||
  1760. segmentIndex == 0 && reversed)
  1761. {
  1762. // Draw second segment
  1763. intersectionPoint.dataPoint = firstPoint.dataPoint;
  1764. intersectionPoint.index = firstPoint.index;
  1765. segmentPath = this.Draw3DSurface(
  1766. area, matrix, lightStyle, surfaceName, positionZ, depth,
  1767. (!firstSegmentVisible && segmentNumber != 3) ? backColor : cutSurfaceBackColor,
  1768. (!firstSegmentVisible && segmentNumber != 3) ? borderColor : cutSurfaceBorderColor,
  1769. borderWidth, borderDashStyle,
  1770. intersectionPoint, secondPoint,
  1771. points, pointIndex, tension, operationType, lineSegmentType,
  1772. forceThinBorder, forceThickBorder, reversedSeriesOrder,
  1773. multiSeries, yValueIndex, clipInsideArea);
  1774. }
  1775. // Add segment path
  1776. if(resultPath != null && segmentPath != null && segmentPath.PointCount > 0)
  1777. {
  1778. resultPath.SetMarkers();
  1779. resultPath.AddPath(segmentPath, true);
  1780. }
  1781. }
  1782. // Restore previous y positions
  1783. firstPoint.yPosition = prevFirstPointY;
  1784. secondPoint.yPosition = prevSecondPointY;
  1785. return resultPath;
  1786. }
  1787. }
  1788. //**********************************************************************
  1789. //** Prepare, transform polygon coordinates
  1790. //**********************************************************************
  1791. // Define 4 points polygon
  1792. Point3D [] points3D = new Point3D[4];
  1793. points3D[0] = new Point3D((float)firstPoint.xPosition, (float)firstPoint.yPosition, positionZ + depth);
  1794. points3D[1] = new Point3D((float)secondPoint.xPosition, (float)secondPoint.yPosition, positionZ + depth);
  1795. points3D[2] = new Point3D((float)secondPoint.xPosition, (float)secondPoint.yPosition, positionZ);
  1796. points3D[3] = new Point3D((float)firstPoint.xPosition, (float)firstPoint.yPosition, positionZ);
  1797. // Transform coordinates
  1798. matrix.TransformPoints( points3D );
  1799. // Get absolute coordinates and create array of PointF
  1800. PointF[] polygonPoints = new PointF[4];
  1801. polygonPoints[0] = GetAbsolutePoint(points3D[0].PointF);
  1802. polygonPoints[1] = GetAbsolutePoint(points3D[1].PointF);
  1803. polygonPoints[2] = GetAbsolutePoint(points3D[2].PointF);
  1804. polygonPoints[3] = GetAbsolutePoint(points3D[3].PointF);
  1805. //**********************************************************************
  1806. //** Define drawing colors
  1807. //**********************************************************************
  1808. bool topIsVisible = IsSurfaceVisible( points3D[0], points3D[1], points3D[2]);
  1809. Color polygonColor = matrix.GetPolygonLight( points3D, backColor, topIsVisible, area.Area3DStyle.Rotation, surfaceName, area.ReverseSeriesOrder );
  1810. Color surfaceBorderColor = borderColor;
  1811. if(surfaceBorderColor == Color.Empty)
  1812. {
  1813. // If border color is emty use color slightly darker than main back color
  1814. surfaceBorderColor = ChartGraphics.GetGradientColor( backColor, Color.Black, 0.2 );
  1815. }
  1816. //**********************************************************************
  1817. //** Draw elements if required.
  1818. //**********************************************************************
  1819. Pen thinBorderPen = new Pen(surfaceBorderColor, 1);
  1820. if(drawElements)
  1821. {
  1822. // Draw the polygon
  1823. if(backColor != Color.Transparent)
  1824. {
  1825. // Remember SmoothingMode and turn off anti aliasing
  1826. SmoothingMode oldSmoothingMode = SmoothingMode;
  1827. SmoothingMode = SmoothingMode.Default;
  1828. // Draw the polygon
  1829. using (Brush brush = new SolidBrush(polygonColor))
  1830. {
  1831. FillPolygon(brush, polygonPoints);
  1832. }
  1833. // Return old smoothing mode
  1834. SmoothingMode = oldSmoothingMode;
  1835. }
  1836. // Draw thin polygon border of darker color
  1837. if(forceThinBorder || forceThickBorder)
  1838. {
  1839. if(forceThickBorder)
  1840. {
  1841. Pen linePen = new Pen(surfaceBorderColor, borderWidth);
  1842. linePen.StartCap = LineCap.Round;
  1843. linePen.EndCap = LineCap.Round;
  1844. DrawLine(linePen, polygonPoints[0], polygonPoints[1]);
  1845. DrawLine(linePen, polygonPoints[2], polygonPoints[3]);
  1846. DrawLine(linePen, polygonPoints[3], polygonPoints[0]);
  1847. DrawLine(linePen, polygonPoints[1], polygonPoints[2]);
  1848. }
  1849. else
  1850. {
  1851. // Front & Back lines
  1852. DrawLine(thinBorderPen, polygonPoints[0], polygonPoints[1]);
  1853. DrawLine(thinBorderPen, polygonPoints[2], polygonPoints[3]);
  1854. if(lineSegmentType == LineSegmentType.First)
  1855. {
  1856. // Left line
  1857. DrawLine(thinBorderPen, polygonPoints[3], polygonPoints[0]);
  1858. }
  1859. else if(lineSegmentType == LineSegmentType.Last)
  1860. {
  1861. // Right Line
  1862. DrawLine(thinBorderPen, polygonPoints[1], polygonPoints[2]);
  1863. }
  1864. else
  1865. {
  1866. // Left & Right lines
  1867. DrawLine(thinBorderPen, polygonPoints[3], polygonPoints[0]);
  1868. DrawLine(thinBorderPen, polygonPoints[1], polygonPoints[2]);
  1869. }
  1870. }
  1871. }
  1872. else
  1873. {
  1874. // Draw thin polygon border of same color (solves anti-aliasing issues)
  1875. if(polygonColor.A == 255)
  1876. {
  1877. DrawPolygon(new Pen(polygonColor, 1), polygonPoints);
  1878. }
  1879. // Draw thin Front & Back lines
  1880. DrawLine(thinBorderPen, polygonPoints[0], polygonPoints[1]);
  1881. DrawLine(thinBorderPen, polygonPoints[2], polygonPoints[3]);
  1882. }
  1883. }
  1884. //**********************************************************************
  1885. //** Draw thick border line on visible sides
  1886. //**********************************************************************
  1887. Pen thickBorderPen = null;
  1888. if(borderWidth > 1 && !forceThickBorder)
  1889. {
  1890. // Create thick border line pen
  1891. thickBorderPen = new Pen(surfaceBorderColor, borderWidth);
  1892. thickBorderPen.StartCap = LineCap.Round;
  1893. thickBorderPen.EndCap = LineCap.Round;
  1894. //****************************************************************
  1895. //** Switch first and second points.
  1896. //****************************************************************
  1897. if(firstPoint.index > secondPoint.index)
  1898. {
  1899. DataPoint3D tempPoint = firstPoint;
  1900. firstPoint = secondPoint;
  1901. secondPoint = tempPoint;
  1902. }
  1903. //**********************************************************************
  1904. //** Check if there are visible (non-empty) lines to the left & right
  1905. //** of the current line.
  1906. //**********************************************************************
  1907. // Get visibility of bounding rectangle
  1908. float minX = (float)Math.Min(points3D[0].X, points3D[1].X);
  1909. float minY = (float)Math.Min(points3D[0].Y, points3D[1].Y);
  1910. float maxX = (float)Math.Max(points3D[0].X, points3D[1].X);
  1911. float maxY = (float)Math.Max(points3D[0].Y, points3D[1].Y);
  1912. RectangleF position = new RectangleF(minX, minY, maxX - minX, maxY - minY);
  1913. SurfaceNames visibleSurfaces = GetVisibleSurfaces(position,positionZ,depth,matrix);
  1914. // Check left line visibility
  1915. bool thickBorderOnLeft = false;
  1916. bool thickBorderOnRight = false;
  1917. if(lineSegmentType != LineSegmentType.Middle)
  1918. {
  1919. LineSegmentType tempLineSegmentType = LineSegmentType.Single;
  1920. // Check left line visibility
  1921. thickBorderOnLeft = (ChartGraphics.ShouldDrawLineChartSurface(
  1922. area,
  1923. reversedSeriesOrder,
  1924. SurfaceNames.Left,
  1925. visibleSurfaces,
  1926. polygonColor,
  1927. points,
  1928. firstPoint,
  1929. secondPoint,
  1930. multiSeries,
  1931. ref tempLineSegmentType) == 2);
  1932. // Check right line visibility
  1933. thickBorderOnRight = (ChartGraphics.ShouldDrawLineChartSurface(
  1934. area,
  1935. reversedSeriesOrder,
  1936. SurfaceNames.Right,
  1937. visibleSurfaces,
  1938. polygonColor,
  1939. points,
  1940. firstPoint,
  1941. secondPoint,
  1942. multiSeries,
  1943. ref tempLineSegmentType) == 2);
  1944. }
  1945. // Switch left & right border if series is reversed
  1946. if(reversedSeriesOrder)
  1947. {
  1948. bool tempVal = thickBorderOnLeft;
  1949. thickBorderOnLeft = thickBorderOnRight;
  1950. thickBorderOnRight = tempVal;
  1951. }
  1952. // Draw thick border for single segment lines only
  1953. // or for the first & last segment
  1954. if(lineSegmentType != LineSegmentType.First && lineSegmentType != LineSegmentType.Single)
  1955. {
  1956. thickBorderOnLeft = false;
  1957. }
  1958. if(lineSegmentType != LineSegmentType.Last && lineSegmentType != LineSegmentType.Single)
  1959. {
  1960. thickBorderOnRight = false;
  1961. }
  1962. //**********************************************************************
  1963. //** Draw border on the front side of line surface (only when visible)
  1964. //**********************************************************************
  1965. if( matrix.Perspective != 0 ||
  1966. (matrix.AngleX != 90 && matrix.AngleX != -90 &&
  1967. matrix.AngleY != 90 && matrix.AngleY != -90 &&
  1968. matrix.AngleY != 180 && matrix.AngleY != -180))
  1969. {
  1970. // Draw thick line on the front side of the line surface
  1971. if(drawElements)
  1972. {
  1973. DrawLine(
  1974. thickBorderPen,
  1975. (float)Math.Round(polygonPoints[0].X),
  1976. (float)Math.Round(polygonPoints[0].Y),
  1977. (float)Math.Round(polygonPoints[1].X),
  1978. (float)Math.Round(polygonPoints[1].Y) );
  1979. }
  1980. // Calculate path for selection
  1981. if(resultPath != null)
  1982. {
  1983. // Add front line to the path
  1984. resultPath.AddLine(
  1985. (float)Math.Round(polygonPoints[0].X),
  1986. (float)Math.Round(polygonPoints[0].Y),
  1987. (float)Math.Round(polygonPoints[1].X),
  1988. (float)Math.Round(polygonPoints[1].Y));
  1989. }
  1990. }
  1991. //**********************************************************************
  1992. //** Draw border on the left side of line surface (only when visible)
  1993. //**********************************************************************
  1994. // Use flat end for Right & Left border
  1995. thickBorderPen.EndCap = LineCap.Flat;
  1996. // Draw border on the left side
  1997. if (matrix.Perspective != 0 || (matrix.AngleX != 90 && matrix.AngleX != -90))
  1998. {
  1999. if(thickBorderOnLeft)
  2000. {
  2001. if(drawElements)
  2002. {
  2003. DrawLine(
  2004. thickBorderPen,
  2005. (float)Math.Round(polygonPoints[3].X),
  2006. (float)Math.Round(polygonPoints[3].Y),
  2007. (float)Math.Round(polygonPoints[0].X),
  2008. (float)Math.Round(polygonPoints[0].Y) );
  2009. }
  2010. // Calculate path for selection
  2011. if(resultPath != null)
  2012. {
  2013. // Add left line to the path
  2014. resultPath.AddLine(
  2015. (float)Math.Round(polygonPoints[3].X),
  2016. (float)Math.Round(polygonPoints[3].Y),
  2017. (float)Math.Round(polygonPoints[0].X),
  2018. (float)Math.Round(polygonPoints[0].Y));
  2019. }
  2020. }
  2021. }
  2022. //**********************************************************************
  2023. //** Draw border on the right side of the line surface
  2024. //**********************************************************************
  2025. if (matrix.Perspective != 0 || (matrix.AngleX != 90 && matrix.AngleX != -90))
  2026. {
  2027. if(thickBorderOnRight)
  2028. {
  2029. if(drawElements)
  2030. {
  2031. DrawLine(
  2032. thickBorderPen,
  2033. (float)Math.Round(polygonPoints[1].X),
  2034. (float)Math.Round(polygonPoints[1].Y),
  2035. (float)Math.Round(polygonPoints[2].X),
  2036. (float)Math.Round(polygonPoints[2].Y) );
  2037. }
  2038. // Calculate path for selection
  2039. if(resultPath != null)
  2040. {
  2041. // Add right line to the path
  2042. resultPath.AddLine(
  2043. (float)Math.Round(polygonPoints[1].X),
  2044. (float)Math.Round(polygonPoints[1].Y),
  2045. (float)Math.Round(polygonPoints[2].X),
  2046. (float)Math.Round(polygonPoints[2].Y));
  2047. }
  2048. }
  2049. }
  2050. }
  2051. //**********************************************************************
  2052. // Redraw front line of the previuos line segment.
  2053. // Solves 3D visibility problem between wide border line and line surface.
  2054. //**********************************************************************
  2055. if( area.Area3DStyle.Perspective == 0 )
  2056. {
  2057. if(frontLinePoint1 != PointF.Empty && frontLinePen != null)
  2058. {
  2059. // Draw line
  2060. DrawLine(
  2061. frontLinePen,
  2062. (float)Math.Round(frontLinePoint1.X),
  2063. (float)Math.Round(frontLinePoint1.Y),
  2064. (float)Math.Round(frontLinePoint2.X),
  2065. (float)Math.Round(frontLinePoint2.Y) );
  2066. // Reset line properties
  2067. frontLinePen = null;
  2068. frontLinePoint1 = PointF.Empty;
  2069. frontLinePoint2 = PointF.Empty;
  2070. }
  2071. //**********************************************************************
  2072. //** Check if front line should be redrawn whith the next segment.
  2073. //**********************************************************************
  2074. if(drawElements)
  2075. {
  2076. frontLinePen = (borderWidth > 1) ? thickBorderPen : thinBorderPen;
  2077. frontLinePoint1 = polygonPoints[0];
  2078. frontLinePoint2 = polygonPoints[1];
  2079. }
  2080. }
  2081. //**********************************************************************
  2082. //** Calculate path for selection
  2083. //**********************************************************************
  2084. if(resultPath != null)
  2085. {
  2086. // Widen all the lines currently in the path
  2087. if(thickBorderPen != null)
  2088. {
  2089. try
  2090. {
  2091. resultPath.Widen(thickBorderPen);
  2092. }
  2093. catch (OutOfMemoryException)
  2094. {
  2095. // GraphicsPath.Widen incorrectly throws OutOfMemoryException
  2096. // catching here and reacting by not widening
  2097. }
  2098. catch (ArgumentException)
  2099. {
  2100. }
  2101. }
  2102. // Add polygon to the path
  2103. resultPath.AddPolygon(polygonPoints);
  2104. }
  2105. return resultPath;
  2106. }
  2107. /// <summary>
  2108. /// Helper method, which indicates if area chart surface should be drawn or not.
  2109. /// </summary>
  2110. /// <param name="area">Chart area object.</param>
  2111. /// <param name="reversedSeriesOrder">Series are drawn in reversed order.</param>
  2112. /// <param name="surfaceName">Surface name.</param>
  2113. /// <param name="boundaryRectVisibleSurfaces">Visible surfaces of the bounding rectangle.</param>
  2114. /// <param name="color">Point back color.</param>
  2115. /// <param name="points">Array of all points.</param>
  2116. /// <param name="firstPoint">First point.</param>
  2117. /// <param name="secondPoint">Second point.</param>
  2118. /// <param name="multiSeries">Indicates that multiple series are painted at the same time (stacked or side-by-side).</param>
  2119. /// <param name="lineSegmentType">Returns line segment type.</param>
  2120. /// <returns>Function retrns 0, 1 or 2. 0 - Do not draw surface, 1 - draw on the back, 2 - draw in front.</returns>
  2121. static internal int ShouldDrawLineChartSurface(
  2122. ChartArea area,
  2123. bool reversedSeriesOrder,
  2124. SurfaceNames surfaceName,
  2125. SurfaceNames boundaryRectVisibleSurfaces,
  2126. Color color,
  2127. ArrayList points,
  2128. DataPoint3D firstPoint,
  2129. DataPoint3D secondPoint,
  2130. bool multiSeries,
  2131. ref LineSegmentType lineSegmentType)
  2132. {
  2133. int result = 0;
  2134. Series series = firstPoint.dataPoint.series;
  2135. // Set active horizontal/vertical axis
  2136. Axis hAxis = (series.XAxisType == AxisType.Primary) ? area.AxisX : area.AxisX2;
  2137. double hAxisMin = hAxis.ViewMinimum;
  2138. double hAxisMax = hAxis.ViewMaximum;
  2139. //****************************************************************
  2140. //** Check if data point and it's neigbours have non-transparent
  2141. //** colors.
  2142. //****************************************************************
  2143. // Check if point main color has transparency
  2144. bool transparent = color.A != 255;
  2145. // Check if points on the left and right side exsit and are transparent
  2146. bool leftPointVisible = false;
  2147. bool rightPointVisible = false;
  2148. if( surfaceName == SurfaceNames.Left )
  2149. {
  2150. // Find Left point
  2151. DataPoint3D leftPoint = null, leftPointAttr = null;
  2152. int pointArrayIndex = int.MinValue;
  2153. if(!reversedSeriesOrder)
  2154. {
  2155. leftPoint = ChartGraphics.FindPointByIndex(points, Math.Min(firstPoint.index, secondPoint.index) - 1, (multiSeries) ? secondPoint : null, ref pointArrayIndex);
  2156. leftPointAttr = ChartGraphics.FindPointByIndex(points, Math.Min(firstPoint.index, secondPoint.index), (multiSeries) ? secondPoint : null, ref pointArrayIndex);
  2157. }
  2158. else
  2159. {
  2160. leftPoint = ChartGraphics.FindPointByIndex(points, Math.Max(firstPoint.index, secondPoint.index) + 1, (multiSeries) ? secondPoint : null, ref pointArrayIndex);
  2161. leftPointAttr = leftPoint;
  2162. }
  2163. if(leftPoint != null)
  2164. {
  2165. if(leftPointAttr.dataPoint.IsEmpty)
  2166. {
  2167. if(leftPointAttr.dataPoint.series.EmptyPointStyle.Color == color ||
  2168. leftPointAttr.dataPoint.series.EmptyPointStyle.Color.A == 255)
  2169. {
  2170. leftPointVisible = true;
  2171. }
  2172. }
  2173. else
  2174. {
  2175. if(leftPointAttr.dataPoint.Color == color ||
  2176. leftPointAttr.dataPoint.Color.A == 255)
  2177. {
  2178. leftPointVisible = true;
  2179. }
  2180. }
  2181. // Check if found point is outside the scaleView
  2182. double xValue = (leftPoint.indexedSeries) ? leftPoint.index : leftPoint.dataPoint.XValue;
  2183. if(xValue > hAxisMax || xValue < hAxisMin)
  2184. {
  2185. DataPoint3D currentPoint = null;
  2186. if(reversedSeriesOrder)
  2187. {
  2188. currentPoint = (firstPoint.index > secondPoint.index) ? firstPoint : secondPoint;
  2189. }
  2190. else
  2191. {
  2192. currentPoint = (firstPoint.index < secondPoint.index) ? firstPoint : secondPoint;
  2193. }
  2194. double currentXValue = (currentPoint.indexedSeries) ? currentPoint.index : currentPoint.dataPoint.XValue;
  2195. if(currentXValue > hAxisMax || currentXValue < hAxisMin)
  2196. {
  2197. leftPointVisible = false;
  2198. }
  2199. }
  2200. }
  2201. }
  2202. // Find Right point
  2203. if( surfaceName == SurfaceNames.Right )
  2204. {
  2205. DataPoint3D rightPoint = null, rightPointAttr = null;
  2206. int pointArrayIndex = int.MinValue;
  2207. if(!reversedSeriesOrder)
  2208. {
  2209. rightPoint = ChartGraphics.FindPointByIndex(points, Math.Max(firstPoint.index, secondPoint.index) + 1, (multiSeries) ? secondPoint : null, ref pointArrayIndex);
  2210. rightPointAttr = rightPoint;
  2211. }
  2212. else
  2213. {
  2214. rightPoint = ChartGraphics.FindPointByIndex(points, Math.Min(firstPoint.index, secondPoint.index) - 1, (multiSeries) ? secondPoint : null, ref pointArrayIndex);
  2215. rightPointAttr = ChartGraphics.FindPointByIndex(points, Math.Min(firstPoint.index, secondPoint.index), (multiSeries) ? secondPoint : null, ref pointArrayIndex);
  2216. }
  2217. if(rightPoint != null)
  2218. {
  2219. if(rightPointAttr.dataPoint.IsEmpty)
  2220. {
  2221. if(rightPointAttr.dataPoint.series.EmptyPointStyle.Color == color ||
  2222. rightPointAttr.dataPoint.series.EmptyPointStyle.Color.A == 255)
  2223. {
  2224. rightPointVisible = true;
  2225. }
  2226. }
  2227. else
  2228. {
  2229. if(rightPointAttr.dataPoint.Color == color ||
  2230. rightPointAttr.dataPoint.Color.A == 255)
  2231. {
  2232. rightPointVisible = true;
  2233. }
  2234. }
  2235. // Check if found point is outside the scaleView
  2236. double xValue = (rightPoint.indexedSeries) ? rightPoint.index : rightPoint.dataPoint.XValue;
  2237. if(xValue > hAxisMax || xValue < hAxisMin)
  2238. {
  2239. DataPoint3D currentPoint = null;
  2240. if(reversedSeriesOrder)
  2241. {
  2242. currentPoint = (firstPoint.index > secondPoint.index) ? firstPoint : secondPoint;
  2243. }
  2244. else
  2245. {
  2246. currentPoint = (firstPoint.index < secondPoint.index) ? firstPoint : secondPoint;
  2247. }
  2248. double currentXValue = (currentPoint.indexedSeries) ? currentPoint.index : currentPoint.dataPoint.XValue;
  2249. if(currentXValue > hAxisMax || currentXValue < hAxisMin)
  2250. {
  2251. rightPointVisible = false;
  2252. }
  2253. }
  2254. }
  2255. }
  2256. //****************************************************************
  2257. //** Get line segment
  2258. //****************************************************************
  2259. if( surfaceName == SurfaceNames.Left && !leftPointVisible)
  2260. {
  2261. if(lineSegmentType == LineSegmentType.Middle)
  2262. {
  2263. lineSegmentType = LineSegmentType.First;
  2264. }
  2265. else if(lineSegmentType == LineSegmentType.Last)
  2266. {
  2267. lineSegmentType = LineSegmentType.Single;
  2268. }
  2269. }
  2270. if( surfaceName == SurfaceNames.Right && !rightPointVisible)
  2271. {
  2272. if(lineSegmentType == LineSegmentType.Middle)
  2273. {
  2274. lineSegmentType = LineSegmentType.Last;
  2275. }
  2276. else if(lineSegmentType == LineSegmentType.First)
  2277. {
  2278. lineSegmentType = LineSegmentType.Single;
  2279. }
  2280. }
  2281. //****************************************************************
  2282. //** Check surfaces visibility
  2283. //****************************************************************
  2284. if( surfaceName == SurfaceNames.Top )
  2285. {
  2286. result = ((boundaryRectVisibleSurfaces & SurfaceNames.Top) == SurfaceNames.Top) ? 2 : 1;
  2287. }
  2288. if( surfaceName == SurfaceNames.Bottom )
  2289. {
  2290. result = ((boundaryRectVisibleSurfaces & SurfaceNames.Bottom) == SurfaceNames.Bottom) ? 2 : 1;
  2291. // Draw invisible bottom surface only if chart is transparent
  2292. if(result == 1 && !transparent)
  2293. {
  2294. result = 0;
  2295. }
  2296. }
  2297. if( surfaceName == SurfaceNames.Front )
  2298. {
  2299. result = ((boundaryRectVisibleSurfaces & SurfaceNames.Front) == SurfaceNames.Front) ? 2 : 1;
  2300. // Draw invisible front surface only if chart is transparent
  2301. if(result == 1 && !transparent)
  2302. {
  2303. result = 0;
  2304. }
  2305. }
  2306. if( surfaceName == SurfaceNames.Back )
  2307. {
  2308. result = ((boundaryRectVisibleSurfaces & SurfaceNames.Back) == SurfaceNames.Back) ? 2 : 1;
  2309. // Draw invisible back surface only if chart is transparent
  2310. if(result == 1 && !transparent)
  2311. {
  2312. result = 0;
  2313. }
  2314. }
  2315. if( surfaceName == SurfaceNames.Left )
  2316. {
  2317. result = ((boundaryRectVisibleSurfaces & SurfaceNames.Left) == SurfaceNames.Left) ? 2 : 1;
  2318. // Draw invisible left surface only if point to the left is transparent
  2319. if(leftPointVisible)
  2320. {
  2321. result = 0;
  2322. }
  2323. }
  2324. if( surfaceName == SurfaceNames.Right )
  2325. {
  2326. result = ((boundaryRectVisibleSurfaces & SurfaceNames.Right) == SurfaceNames.Right) ? 2 : 1;
  2327. // Draw invisible right surface only if point to the right is transparent
  2328. if(rightPointVisible)
  2329. {
  2330. result = 0;
  2331. }
  2332. }
  2333. return result;
  2334. }
  2335. /// <summary>
  2336. /// Helper method which finds point in the list by it's real index.
  2337. /// </summary>
  2338. /// <param name="points">List of points.</param>
  2339. /// <param name="index">Required index.</param>
  2340. /// <param name="neighborDataPoint">Neighbor point of the same series.</param>
  2341. /// <param name="neighborPointIndex">Neighbor point index in the array list.</param>
  2342. /// <returns>Data point found.</returns>
  2343. internal static DataPoint3D FindPointByIndex(ArrayList points, int index, DataPoint3D neighborDataPoint, ref int neighborPointIndex)
  2344. {
  2345. // Try to look around the neighbor point index
  2346. if(neighborPointIndex != int.MinValue)
  2347. {
  2348. // Try getting the next point
  2349. if(neighborPointIndex < (points.Count - 2))
  2350. {
  2351. DataPoint3D point = (DataPoint3D)points[neighborPointIndex + 1];
  2352. // Check required point index for the first point
  2353. if( point.index == index &&
  2354. (neighborDataPoint == null || String.Compare(neighborDataPoint.dataPoint.series.Name, point.dataPoint.series.Name, StringComparison.Ordinal) == 0))
  2355. {
  2356. ++neighborPointIndex;
  2357. return point;
  2358. }
  2359. }
  2360. // Try getting the prev point
  2361. if(neighborPointIndex > 0)
  2362. {
  2363. DataPoint3D point = (DataPoint3D)points[neighborPointIndex - 1];
  2364. // Check required point index for the first point
  2365. if( point.index == index &&
  2366. (neighborDataPoint == null || String.Compare(neighborDataPoint.dataPoint.series.Name, point.dataPoint.series.Name, StringComparison.Ordinal) == 0))
  2367. {
  2368. --neighborPointIndex;
  2369. return point;
  2370. }
  2371. }
  2372. }
  2373. // Loop through all points
  2374. neighborPointIndex = 0;
  2375. foreach(DataPoint3D point3D in points)
  2376. {
  2377. // Check required point index for the first point
  2378. if(point3D.index == index)
  2379. {
  2380. // Check if point belongs to the same series
  2381. if(neighborDataPoint != null)
  2382. {
  2383. if (String.Compare(neighborDataPoint.dataPoint.series.Name, point3D.dataPoint.series.Name, StringComparison.Ordinal) != 0)
  2384. {
  2385. ++neighborPointIndex;
  2386. continue;
  2387. }
  2388. }
  2389. // Point found
  2390. return (DataPoint3D)point3D;
  2391. }
  2392. ++neighborPointIndex;
  2393. }
  2394. // Data point was not found
  2395. return null;
  2396. }
  2397. #endregion
  2398. #region 3D Rectangle drawing methods
  2399. /// <summary>
  2400. /// Function is used to calculate the coordinates of the 2D rectangle in 3D space
  2401. /// and either draw it or/and calculate the bounding path for selection.
  2402. /// </summary>
  2403. /// <param name="position">Position of 2D rectangle.</param>
  2404. /// <param name="positionZ">Z position of the back side of the 3D rectangle.</param>
  2405. /// <param name="depth">Depth of the 3D rectangle.</param>
  2406. /// <param name="matrix">Coordinate transformation matrix.</param>
  2407. /// <param name="lightStyle">LightStyle style (None, Simplistic, Realistic).</param>
  2408. /// <param name="backColor">Color of rectangle</param>
  2409. /// <param name="borderColor">Border Color</param>
  2410. /// <param name="borderWidth">Border Width</param>
  2411. /// <param name="borderDashStyle">Border Style</param>
  2412. /// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
  2413. /// <returns>Returns elemnt shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
  2414. internal GraphicsPath Fill3DRectangle(
  2415. RectangleF position,
  2416. float positionZ,
  2417. float depth,
  2418. Matrix3D matrix,
  2419. LightStyle lightStyle,
  2420. Color backColor,
  2421. Color borderColor,
  2422. int borderWidth,
  2423. ChartDashStyle borderDashStyle,
  2424. DrawingOperationTypes operationType)
  2425. {
  2426. return Fill3DRectangle(
  2427. position,
  2428. positionZ,
  2429. depth,
  2430. matrix,
  2431. lightStyle,
  2432. backColor,
  2433. 0f,
  2434. 0f,
  2435. borderColor,
  2436. borderWidth,
  2437. borderDashStyle,
  2438. BarDrawingStyle.Default,
  2439. false,
  2440. operationType);
  2441. }
  2442. /// <summary>
  2443. /// Function is used to calculate the coordinates of the 2D rectangle in 3D space
  2444. /// and either draw it or/and calculate the bounding path for selection.
  2445. /// </summary>
  2446. /// <param name="position">Position of 2D rectangle.</param>
  2447. /// <param name="positionZ">Z position of the back side of the 3D rectangle.</param>
  2448. /// <param name="depth">Depth of the 3D rectangle.</param>
  2449. /// <param name="matrix">Coordinate transformation matrix.</param>
  2450. /// <param name="lightStyle">LightStyle style (None, Simplistic, Realistic).</param>
  2451. /// <param name="backColor">Color of rectangle</param>
  2452. /// <param name="topRightDarkening">Top (or right in bar chart) darkening effect.</param>
  2453. /// <param name="bottomLeftDarkening">Bottom (or left in bar chart) darkening effect.</param>
  2454. /// <param name="borderColor">Border Color</param>
  2455. /// <param name="borderWidth">Border Width</param>
  2456. /// <param name="borderDashStyle">Border Style</param>
  2457. /// <param name="barDrawingStyle">Bar drawing style.</param>
  2458. /// <param name="veticalOrientation">Defines if bar is vertical or horizontal.</param>
  2459. /// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
  2460. /// <returns>Returns elemnt shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
  2461. internal GraphicsPath Fill3DRectangle(
  2462. RectangleF position,
  2463. float positionZ,
  2464. float depth,
  2465. Matrix3D matrix,
  2466. LightStyle lightStyle,
  2467. Color backColor,
  2468. float topRightDarkening,
  2469. float bottomLeftDarkening,
  2470. Color borderColor,
  2471. int borderWidth,
  2472. ChartDashStyle borderDashStyle,
  2473. BarDrawingStyle barDrawingStyle,
  2474. bool veticalOrientation,
  2475. DrawingOperationTypes operationType)
  2476. {
  2477. // Check if special drawing is required
  2478. if(barDrawingStyle == BarDrawingStyle.Cylinder)
  2479. {
  2480. // Draw as 3D cylinder
  2481. return Fill3DRectangleAsCylinder(
  2482. position,
  2483. positionZ,
  2484. depth,
  2485. matrix,
  2486. lightStyle,
  2487. backColor,
  2488. topRightDarkening,
  2489. bottomLeftDarkening,
  2490. borderColor,
  2491. borderWidth,
  2492. borderDashStyle,
  2493. veticalOrientation,
  2494. operationType);
  2495. }
  2496. // Declare variables
  2497. Point3D[] cubePoints = new Point3D[8];
  2498. GraphicsPath resultPath = ((operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
  2499. ? new GraphicsPath() : null;
  2500. // Front Side
  2501. cubePoints[0] = new Point3D( position.X, position.Y, positionZ + depth );
  2502. cubePoints[1] = new Point3D( position.X, position.Bottom, positionZ + depth );
  2503. cubePoints[2] = new Point3D( position.Right, position.Bottom, positionZ + depth );
  2504. cubePoints[3] = new Point3D( position.Right, position.Y, positionZ + depth );
  2505. // Back Side
  2506. cubePoints[4] = new Point3D( position.X, position.Y, positionZ );
  2507. cubePoints[5] = new Point3D( position.X, position.Bottom, positionZ );
  2508. cubePoints[6] = new Point3D( position.Right, position.Bottom, positionZ );
  2509. cubePoints[7] = new Point3D( position.Right, position.Y, positionZ );
  2510. // Tranform cube coordinates
  2511. matrix.TransformPoints( cubePoints );
  2512. // For lightStyle style Non, Border color always exist.
  2513. if( lightStyle == LightStyle.None &&
  2514. (borderWidth == 0 || borderDashStyle == ChartDashStyle.NotSet || borderColor == Color.Empty) )
  2515. {
  2516. borderColor = ChartGraphics.GetGradientColor( backColor, Color.Black, 0.5 );
  2517. }
  2518. // Get surface colors
  2519. Color frontLightColor, leftLightColor, topLightColor, backLightColor, rightLightColor, bottomLightColor;
  2520. matrix.GetLight( backColor, out frontLightColor, out backLightColor, out leftLightColor, out rightLightColor, out topLightColor, out bottomLightColor );
  2521. // Darken colors by specified values
  2522. if(topRightDarkening != 0f)
  2523. {
  2524. if(veticalOrientation)
  2525. {
  2526. topLightColor = ChartGraphics.GetGradientColor(topLightColor, Color.Black, topRightDarkening);
  2527. }
  2528. else
  2529. {
  2530. rightLightColor = ChartGraphics.GetGradientColor(rightLightColor, Color.Black, topRightDarkening);
  2531. }
  2532. }
  2533. if(bottomLeftDarkening != 0f)
  2534. {
  2535. if(veticalOrientation)
  2536. {
  2537. bottomLightColor = ChartGraphics.GetGradientColor(bottomLightColor, Color.Black, bottomLeftDarkening);
  2538. }
  2539. else
  2540. {
  2541. leftLightColor = ChartGraphics.GetGradientColor(leftLightColor, Color.Black, bottomLeftDarkening);
  2542. }
  2543. }
  2544. // Check visible surfaces
  2545. SurfaceNames visibleSurfaces = GetVisibleSurfacesWithPerspective(position,positionZ,depth,matrix);
  2546. // Draw all invisible surfaces first (if semi-transparent color is used)
  2547. for(int drawVisible = 0; drawVisible <= 1; drawVisible++)
  2548. {
  2549. // Do not draw invisible surfaces for solid colors
  2550. if(drawVisible == 0 && backColor.A == 255)
  2551. {
  2552. continue;
  2553. }
  2554. // Check visibility of all surfaces and draw them
  2555. for(int surfaceIndex = (int)SurfaceNames.Front; surfaceIndex <= (int)SurfaceNames.Bottom; surfaceIndex *= 2)
  2556. {
  2557. SurfaceNames currentSurface = (SurfaceNames)surfaceIndex;
  2558. // If width, height or depth of the cube (3DRectangle) is zero graphical path
  2559. // should contain only one surface with 4 points.
  2560. if(depth == 0.0 && currentSurface != SurfaceNames.Front)
  2561. {
  2562. continue;
  2563. }
  2564. if(position.Width == 0.0 && currentSurface != SurfaceNames.Left && currentSurface != SurfaceNames.Right)
  2565. {
  2566. continue;
  2567. }
  2568. if(position.Height == 0.0 && currentSurface != SurfaceNames.Top && currentSurface != SurfaceNames.Bottom)
  2569. {
  2570. continue;
  2571. }
  2572. // Check if surface is visible or semi-transparent color is used
  2573. bool isVisible = (visibleSurfaces & currentSurface) != 0;
  2574. if(isVisible && drawVisible == 1 ||
  2575. !isVisible && drawVisible == 0)
  2576. {
  2577. // Fill surface coordinates and color
  2578. PointF [] pointsSurface = new PointF[4];
  2579. Color surfaceColor = backColor;
  2580. switch(currentSurface)
  2581. {
  2582. case(SurfaceNames.Front):
  2583. surfaceColor = frontLightColor;
  2584. pointsSurface[0] = new PointF(cubePoints[0].X, cubePoints[0].Y);
  2585. pointsSurface[1] = new PointF(cubePoints[1].X, cubePoints[1].Y);
  2586. pointsSurface[2] = new PointF(cubePoints[2].X, cubePoints[2].Y);
  2587. pointsSurface[3] = new PointF(cubePoints[3].X, cubePoints[3].Y);
  2588. break;
  2589. case(SurfaceNames.Back):
  2590. surfaceColor = backLightColor;
  2591. pointsSurface[0] = new PointF(cubePoints[4].X, cubePoints[4].Y);
  2592. pointsSurface[1] = new PointF(cubePoints[5].X, cubePoints[5].Y);
  2593. pointsSurface[2] = new PointF(cubePoints[6].X, cubePoints[6].Y);
  2594. pointsSurface[3] = new PointF(cubePoints[7].X, cubePoints[7].Y);
  2595. break;
  2596. case(SurfaceNames.Left):
  2597. surfaceColor = leftLightColor;
  2598. pointsSurface[0] = new PointF(cubePoints[0].X, cubePoints[0].Y);
  2599. pointsSurface[1] = new PointF(cubePoints[1].X, cubePoints[1].Y);
  2600. pointsSurface[2] = new PointF(cubePoints[5].X, cubePoints[5].Y);
  2601. pointsSurface[3] = new PointF(cubePoints[4].X, cubePoints[4].Y);
  2602. break;
  2603. case(SurfaceNames.Right):
  2604. surfaceColor = rightLightColor;
  2605. pointsSurface[0] = new PointF(cubePoints[3].X, cubePoints[3].Y);
  2606. pointsSurface[1] = new PointF(cubePoints[2].X, cubePoints[2].Y);
  2607. pointsSurface[2] = new PointF(cubePoints[6].X, cubePoints[6].Y);
  2608. pointsSurface[3] = new PointF(cubePoints[7].X, cubePoints[7].Y);
  2609. break;
  2610. case(SurfaceNames.Top):
  2611. surfaceColor = topLightColor;
  2612. pointsSurface[0] = new PointF(cubePoints[0].X, cubePoints[0].Y);
  2613. pointsSurface[1] = new PointF(cubePoints[3].X, cubePoints[3].Y);
  2614. pointsSurface[2] = new PointF(cubePoints[7].X, cubePoints[7].Y);
  2615. pointsSurface[3] = new PointF(cubePoints[4].X, cubePoints[4].Y);
  2616. break;
  2617. case(SurfaceNames.Bottom):
  2618. surfaceColor = bottomLightColor;
  2619. pointsSurface[0] = new PointF(cubePoints[1].X, cubePoints[1].Y);
  2620. pointsSurface[1] = new PointF(cubePoints[2].X, cubePoints[2].Y);
  2621. pointsSurface[2] = new PointF(cubePoints[6].X, cubePoints[6].Y);
  2622. pointsSurface[3] = new PointF(cubePoints[5].X, cubePoints[5].Y);
  2623. break;
  2624. }
  2625. // Covert coordinates to absolute
  2626. for(int pointIndex = 0; pointIndex < pointsSurface.Length; pointIndex++)
  2627. {
  2628. pointsSurface[pointIndex] = GetAbsolutePoint(pointsSurface[pointIndex]);
  2629. }
  2630. // Draw surface
  2631. if( (operationType & DrawingOperationTypes.DrawElement) == DrawingOperationTypes.DrawElement)
  2632. {
  2633. // Draw only completly visible surfaces
  2634. if((visibleSurfaces & currentSurface) != 0)
  2635. {
  2636. using (Brush brush = new SolidBrush(surfaceColor))
  2637. {
  2638. FillPolygon(brush, pointsSurface);
  2639. }
  2640. // Check if any additional drawing should be done
  2641. if(currentSurface == SurfaceNames.Front &&
  2642. barDrawingStyle != BarDrawingStyle.Default &&
  2643. barDrawingStyle != BarDrawingStyle.Cylinder)
  2644. {
  2645. this.DrawBarStyleGradients(matrix, barDrawingStyle, position, positionZ, depth, veticalOrientation);
  2646. }
  2647. }
  2648. // Draw surface border
  2649. using (Pen pen = new Pen(borderColor, borderWidth))
  2650. {
  2651. pen.DashStyle = GetPenStyle(borderDashStyle);
  2652. if (lightStyle != LightStyle.None &&
  2653. (borderWidth == 0 || borderDashStyle == ChartDashStyle.NotSet || borderColor == Color.Empty))
  2654. {
  2655. // Draw line of the same color inside the bar
  2656. pen.Color = surfaceColor;
  2657. pen.Width = 1;
  2658. pen.Alignment = PenAlignment.Inset;
  2659. }
  2660. pen.StartCap = LineCap.Round;
  2661. pen.EndCap = LineCap.Round;
  2662. DrawLine(pen, pointsSurface[0], pointsSurface[1]);
  2663. DrawLine(pen, pointsSurface[1], pointsSurface[2]);
  2664. DrawLine(pen, pointsSurface[2], pointsSurface[3]);
  2665. DrawLine(pen, pointsSurface[3], pointsSurface[0]);
  2666. }
  2667. }
  2668. // Add surface coordinate to the path
  2669. if( (operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
  2670. {
  2671. // Only if surface is completly visible
  2672. if((visibleSurfaces & currentSurface) != 0)
  2673. {
  2674. resultPath.SetMarkers();
  2675. resultPath.AddPolygon(pointsSurface);
  2676. }
  2677. }
  2678. }
  2679. }
  2680. }
  2681. return resultPath;
  2682. }
  2683. /// <summary>
  2684. /// Draws special bar style effect on the front surface of the bar.
  2685. /// </summary>
  2686. /// <param name="matrix">Drawing matrix.</param>
  2687. /// <param name="barDrawingStyle">Bar drawing style.</param>
  2688. /// <param name="position">Position in relative coordinates</param>
  2689. /// <param name="positionZ">Z position.</param>
  2690. /// <param name="depth">Depth.</param>
  2691. /// <param name="isVertical">Defines if bar is vertical or horizontal.</param>
  2692. private void DrawBarStyleGradients(
  2693. Matrix3D matrix,
  2694. BarDrawingStyle barDrawingStyle,
  2695. RectangleF position,
  2696. float positionZ,
  2697. float depth,
  2698. bool isVertical)
  2699. {
  2700. if(barDrawingStyle == BarDrawingStyle.Wedge)
  2701. {
  2702. // Calculate wedge size to fit the rectangle
  2703. RectangleF positionAbs = GetAbsoluteRectangle(position);
  2704. float size = (isVertical) ? positionAbs.Width / 2f : positionAbs.Height / 2f;
  2705. if(isVertical && 2f * size > positionAbs.Height)
  2706. {
  2707. size = positionAbs.Height/2f;
  2708. }
  2709. if(!isVertical && 2f * size > positionAbs.Width)
  2710. {
  2711. size = positionAbs.Width/2f;
  2712. }
  2713. SizeF sizeRel = GetRelativeSize(new SizeF(size, size));
  2714. // Make 3D convertion of the key points
  2715. Point3D[] gradientPoints = new Point3D[6];
  2716. gradientPoints[0] = new Point3D( position.Left, position.Top, positionZ + depth );
  2717. gradientPoints[1] = new Point3D( position.Left, position.Bottom, positionZ + depth );
  2718. gradientPoints[2] = new Point3D( position.Right, position.Bottom, positionZ + depth );
  2719. gradientPoints[3] = new Point3D( position.Right, position.Top, positionZ + depth );
  2720. if(isVertical)
  2721. {
  2722. gradientPoints[4] = new Point3D( position.X + position.Width / 2f, position.Top + sizeRel.Height, positionZ + depth );
  2723. gradientPoints[5] = new Point3D( position.X + position.Width / 2f, position.Bottom - sizeRel.Height, positionZ + depth );
  2724. }
  2725. else
  2726. {
  2727. gradientPoints[4] = new Point3D( position.X + sizeRel.Width, position.Top + position.Height / 2f, positionZ + depth );
  2728. gradientPoints[5] = new Point3D( position.Right - sizeRel.Width, position.Top + position.Height / 2f, positionZ + depth );
  2729. }
  2730. // Tranform cube coordinates
  2731. matrix.TransformPoints( gradientPoints );
  2732. // Convert points to absolute
  2733. PointF [] gradientPointsAbs = new PointF[6];
  2734. for(int index = 0; index < gradientPoints.Length; index++)
  2735. {
  2736. gradientPointsAbs[index] = GetAbsolutePoint(gradientPoints[index].PointF);
  2737. }
  2738. // Draw left/bottom shadow
  2739. using(GraphicsPath path = new GraphicsPath())
  2740. {
  2741. if(isVertical)
  2742. {
  2743. path.AddLine(gradientPointsAbs[4], gradientPointsAbs[5]);
  2744. path.AddLine(gradientPointsAbs[5], gradientPointsAbs[2]);
  2745. path.AddLine(gradientPointsAbs[2], gradientPointsAbs[3]);
  2746. }
  2747. else
  2748. {
  2749. path.AddLine(gradientPointsAbs[4], gradientPointsAbs[5]);
  2750. path.AddLine(gradientPointsAbs[5], gradientPointsAbs[2]);
  2751. path.AddLine(gradientPointsAbs[2], gradientPointsAbs[1]);
  2752. }
  2753. path.CloseAllFigures();
  2754. // Create brush and fill path
  2755. using(SolidBrush brush = new SolidBrush(Color.FromArgb(90, Color.Black)))
  2756. {
  2757. this.FillPath(brush, path);
  2758. }
  2759. }
  2760. // Draw top/right triangle
  2761. using(GraphicsPath path = new GraphicsPath())
  2762. {
  2763. if(isVertical)
  2764. {
  2765. path.AddLine(gradientPointsAbs[0], gradientPointsAbs[4]);
  2766. path.AddLine(gradientPointsAbs[4], gradientPointsAbs[3]);
  2767. }
  2768. else
  2769. {
  2770. path.AddLine(gradientPointsAbs[3], gradientPointsAbs[5]);
  2771. path.AddLine(gradientPointsAbs[5], gradientPointsAbs[2]);
  2772. }
  2773. // Create brush and fill path
  2774. using(SolidBrush brush = new SolidBrush(Color.FromArgb(50, Color.Black)))
  2775. {
  2776. // Fill shadow path on the left-bottom side of the bar
  2777. this.FillPath(brush, path);
  2778. // Draw Lines
  2779. using(Pen penDark = new Pen(Color.FromArgb(20, Color.Black), 1))
  2780. {
  2781. this.DrawPath(penDark, path);
  2782. this.DrawLine(
  2783. penDark,
  2784. gradientPointsAbs[4],
  2785. gradientPointsAbs[5]);
  2786. }
  2787. // Draw Lines
  2788. using(Pen pen = new Pen(Color.FromArgb(40, Color.White), 1))
  2789. {
  2790. this.DrawPath(pen, path);
  2791. this.DrawLine(
  2792. pen,
  2793. gradientPointsAbs[4],
  2794. gradientPointsAbs[5]);
  2795. }
  2796. }
  2797. }
  2798. // Draw bottom/left triangle
  2799. using(GraphicsPath path = new GraphicsPath())
  2800. {
  2801. if(isVertical)
  2802. {
  2803. path.AddLine(gradientPointsAbs[1], gradientPointsAbs[5]);
  2804. path.AddLine(gradientPointsAbs[5], gradientPointsAbs[2]);
  2805. }
  2806. else
  2807. {
  2808. path.AddLine(gradientPointsAbs[0], gradientPointsAbs[4]);
  2809. path.AddLine(gradientPointsAbs[4], gradientPointsAbs[1]);
  2810. }
  2811. // Create brush
  2812. using(SolidBrush brush = new SolidBrush(Color.FromArgb(50, Color.Black)))
  2813. {
  2814. // Fill shadow path on the left-bottom side of the bar
  2815. this.FillPath(brush, path);
  2816. // Draw edges
  2817. using(Pen penDark = new Pen(Color.FromArgb(20, Color.Black), 1))
  2818. {
  2819. this.DrawPath(penDark, path);
  2820. }
  2821. using(Pen pen = new Pen(Color.FromArgb(40, Color.White), 1))
  2822. {
  2823. this.DrawPath(pen, path);
  2824. }
  2825. }
  2826. }
  2827. }
  2828. else if(barDrawingStyle == BarDrawingStyle.LightToDark)
  2829. {
  2830. // Calculate width of shadows used to create the effect
  2831. RectangleF positionAbs = GetAbsoluteRectangle(position);
  2832. float shadowSizeAbs = 5f;
  2833. if(positionAbs.Width < 6f || positionAbs.Height < 6f)
  2834. {
  2835. shadowSizeAbs = 2f;
  2836. }
  2837. else if(positionAbs.Width < 15f || positionAbs.Height < 15f)
  2838. {
  2839. shadowSizeAbs = 3f;
  2840. }
  2841. SizeF shadowSizeRel = GetRelativeSize(new SizeF(shadowSizeAbs, shadowSizeAbs));
  2842. // Calculate gradient position
  2843. RectangleF gradientRect = position;
  2844. gradientRect.Inflate(-shadowSizeRel.Width, -shadowSizeRel.Height);
  2845. if(isVertical)
  2846. {
  2847. gradientRect.Height = (float)Math.Floor(gradientRect.Height / 3f);
  2848. }
  2849. else
  2850. {
  2851. gradientRect.X = gradientRect.Right - (float)Math.Floor(gradientRect.Width / 3f);
  2852. gradientRect.Width = (float)Math.Floor(gradientRect.Width / 3f);
  2853. }
  2854. // Top gradient
  2855. Point3D[] gradientPoints = new Point3D[4];
  2856. gradientPoints[0] = new Point3D( gradientRect.Left, gradientRect.Top, positionZ + depth );
  2857. gradientPoints[1] = new Point3D( gradientRect.Left, gradientRect.Bottom, positionZ + depth );
  2858. gradientPoints[2] = new Point3D( gradientRect.Right, gradientRect.Bottom, positionZ + depth );
  2859. gradientPoints[3] = new Point3D( gradientRect.Right, gradientRect.Top, positionZ + depth );
  2860. // Tranform cube coordinates
  2861. matrix.TransformPoints( gradientPoints );
  2862. // Convert points to absolute
  2863. PointF [] gradientPointsAbs = new PointF[4];
  2864. for(int index = 0; index < gradientPoints.Length; index++)
  2865. {
  2866. gradientPointsAbs[index] = GetAbsolutePoint(gradientPoints[index].PointF);
  2867. }
  2868. // Create and draw top path
  2869. using(GraphicsPath path = new GraphicsPath())
  2870. {
  2871. path.AddPolygon(gradientPointsAbs);
  2872. RectangleF bounds = path.GetBounds();
  2873. bounds.Width += 1f;
  2874. bounds.Height += 1f;
  2875. // Create brush
  2876. if(bounds.Width > 0f && bounds.Height > 0f)
  2877. {
  2878. using(LinearGradientBrush topBrush = new LinearGradientBrush(
  2879. bounds,
  2880. (!isVertical) ? Color.Transparent : Color.FromArgb(120, Color.White),
  2881. (!isVertical) ? Color.FromArgb(120, Color.White) : Color.Transparent,
  2882. (isVertical) ? LinearGradientMode.Vertical : LinearGradientMode.Horizontal))
  2883. {
  2884. // Fill shadow path on the top side of the bar
  2885. this.FillPath(topBrush, path);
  2886. }
  2887. }
  2888. }
  2889. // Calculate gradient position for the bottom gradient
  2890. gradientRect = position;
  2891. gradientRect.Inflate(-shadowSizeRel.Width, -shadowSizeRel.Height);
  2892. if(isVertical)
  2893. {
  2894. gradientRect.Y = gradientRect.Bottom - (float)Math.Floor(gradientRect.Height / 3f);
  2895. gradientRect.Height = (float)Math.Floor(gradientRect.Height / 3f);
  2896. }
  2897. else
  2898. {
  2899. gradientRect.Width = (float)Math.Floor(gradientRect.Width / 3f);
  2900. }
  2901. // Top gradient
  2902. gradientPoints = new Point3D[4];
  2903. gradientPoints[0] = new Point3D( gradientRect.Left, gradientRect.Top, positionZ + depth );
  2904. gradientPoints[1] = new Point3D( gradientRect.Left, gradientRect.Bottom, positionZ + depth );
  2905. gradientPoints[2] = new Point3D( gradientRect.Right, gradientRect.Bottom, positionZ + depth );
  2906. gradientPoints[3] = new Point3D( gradientRect.Right, gradientRect.Top, positionZ + depth );
  2907. // Tranform cube coordinates
  2908. matrix.TransformPoints( gradientPoints );
  2909. // Convert points to absolute
  2910. gradientPointsAbs = new PointF[4];
  2911. for(int index = 0; index < gradientPoints.Length; index++)
  2912. {
  2913. gradientPointsAbs[index] = GetAbsolutePoint(gradientPoints[index].PointF);
  2914. }
  2915. // Create and draw top path
  2916. using(GraphicsPath path = new GraphicsPath())
  2917. {
  2918. path.AddPolygon(gradientPointsAbs);
  2919. RectangleF bounds = path.GetBounds();
  2920. bounds.Width += 1f;
  2921. bounds.Height += 1f;
  2922. // Create brush
  2923. if(bounds.Width > 0f && bounds.Height > 0f)
  2924. {
  2925. using(LinearGradientBrush topBrush = new LinearGradientBrush(
  2926. bounds,
  2927. (isVertical) ? Color.Transparent : Color.FromArgb(80, Color.Black),
  2928. (isVertical) ? Color.FromArgb(80, Color.Black) : Color.Transparent,
  2929. (isVertical) ? LinearGradientMode.Vertical : LinearGradientMode.Horizontal))
  2930. {
  2931. // Fill shadow path on the top side of the bar
  2932. this.FillPath(topBrush, path);
  2933. }
  2934. }
  2935. }
  2936. }
  2937. else if(barDrawingStyle == BarDrawingStyle.Emboss)
  2938. {
  2939. // Calculate width of shadows used to create the effect
  2940. RectangleF positionAbs = GetAbsoluteRectangle(position);
  2941. float shadowSizeAbs = 4f;
  2942. if(positionAbs.Width < 6f || positionAbs.Height < 6f)
  2943. {
  2944. shadowSizeAbs = 2f;
  2945. }
  2946. else if(positionAbs.Width < 15f || positionAbs.Height < 15f)
  2947. {
  2948. shadowSizeAbs = 3f;
  2949. }
  2950. SizeF shadowSizeRel = GetRelativeSize(new SizeF(shadowSizeAbs, shadowSizeAbs));
  2951. // Left/top Side
  2952. Point3D[] gradientPoints = new Point3D[6];
  2953. gradientPoints[0] = new Point3D( position.Left, position.Bottom, positionZ + depth );
  2954. gradientPoints[1] = new Point3D( position.Left, position.Top, positionZ + depth );
  2955. gradientPoints[2] = new Point3D( position.Right, position.Top, positionZ + depth );
  2956. gradientPoints[3] = new Point3D( position.Right - shadowSizeRel.Width, position.Top + shadowSizeRel.Height, positionZ + depth );
  2957. gradientPoints[4] = new Point3D( position.Left + shadowSizeRel.Width, position.Top + shadowSizeRel.Height, positionZ + depth );
  2958. gradientPoints[5] = new Point3D( position.Left + shadowSizeRel.Width, position.Bottom - shadowSizeRel.Height, positionZ + depth );
  2959. // Tranform cube coordinates
  2960. matrix.TransformPoints( gradientPoints );
  2961. // Convert points to absolute
  2962. PointF [] gradientPointsAbs = new PointF[6];
  2963. for(int index = 0; index < gradientPoints.Length; index++)
  2964. {
  2965. gradientPointsAbs[index] = GetAbsolutePoint(gradientPoints[index].PointF);
  2966. }
  2967. // Create and draw left/top path
  2968. using(GraphicsPath path = new GraphicsPath())
  2969. {
  2970. path.AddPolygon(gradientPointsAbs);
  2971. // Create brush
  2972. using(SolidBrush leftTopBrush = new SolidBrush(Color.FromArgb(100, Color.White)))
  2973. {
  2974. // Fill shadow path on the left-bottom side of the bar
  2975. this.FillPath(leftTopBrush, path);
  2976. }
  2977. }
  2978. // Right/bottom Side
  2979. gradientPoints[0] = new Point3D( position.Right, position.Top, positionZ + depth );
  2980. gradientPoints[1] = new Point3D( position.Right, position.Bottom, positionZ + depth );
  2981. gradientPoints[2] = new Point3D( position.Left, position.Bottom, positionZ + depth );
  2982. gradientPoints[3] = new Point3D( position.Left + shadowSizeRel.Width, position.Bottom - shadowSizeRel.Height, positionZ + depth );
  2983. gradientPoints[4] = new Point3D( position.Right - shadowSizeRel.Width, position.Bottom - shadowSizeRel.Height, positionZ + depth );
  2984. gradientPoints[5] = new Point3D( position.Right - shadowSizeRel.Width, position.Top + shadowSizeRel.Height, positionZ + depth );
  2985. // Tranform cube coordinates
  2986. matrix.TransformPoints( gradientPoints );
  2987. // Convert points to absolute
  2988. for(int index = 0; index < gradientPoints.Length; index++)
  2989. {
  2990. gradientPointsAbs[index] = GetAbsolutePoint(gradientPoints[index].PointF);
  2991. }
  2992. // Create and draw left/top path
  2993. using(GraphicsPath path = new GraphicsPath())
  2994. {
  2995. path.AddPolygon(gradientPointsAbs);
  2996. // Create brush
  2997. using(SolidBrush bottomRightBrush = new SolidBrush(Color.FromArgb(80, Color.Black)))
  2998. {
  2999. // Fill shadow path on the left-bottom side of the bar
  3000. this.FillPath(bottomRightBrush, path);
  3001. }
  3002. }
  3003. }
  3004. }
  3005. #endregion
  3006. #region 3D markers drawing methods
  3007. /// <summary>
  3008. /// Draw marker using absolute coordinates of the center.
  3009. /// </summary>
  3010. /// <param name="matrix">Coordinates transformation matrix.</param>
  3011. /// <param name="lightStyle">LightStyle style (None, Simplistic, Realistic).</param>
  3012. /// <param name="positionZ">Z position of the 3D marker center.</param>
  3013. /// <param name="point">Coordinates of the center.</param>
  3014. /// <param name="markerStyle">Marker style.</param>
  3015. /// <param name="markerSize">Marker size.</param>
  3016. /// <param name="markerColor">Marker color.</param>
  3017. /// <param name="markerBorderColor">Marker border color.</param>
  3018. /// <param name="markerBorderSize">Marker border size.</param>
  3019. /// <param name="markerImage">Marker image name.</param>
  3020. /// <param name="markerImageTransparentColor">Marker image transparent color.</param>
  3021. /// <param name="shadowSize">Marker shadow size.</param>
  3022. /// <param name="shadowColor">Marker shadow color.</param>
  3023. /// <param name="imageScaleRect">Rectangle to which marker image should be scaled.</param>
  3024. /// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
  3025. /// <returns>Returns elemnt shape path if operationType parameter is set to ElementPath, otherwise Null.</returns>
  3026. internal GraphicsPath DrawMarker3D(
  3027. Matrix3D matrix,
  3028. LightStyle lightStyle,
  3029. float positionZ,
  3030. PointF point,
  3031. MarkerStyle markerStyle,
  3032. int markerSize,
  3033. Color markerColor,
  3034. Color markerBorderColor,
  3035. int markerBorderSize,
  3036. string markerImage,
  3037. Color markerImageTransparentColor,
  3038. int shadowSize,
  3039. Color shadowColor,
  3040. RectangleF imageScaleRect,
  3041. DrawingOperationTypes operationType )
  3042. {
  3043. ChartGraphics graph = (ChartGraphics)this;
  3044. GraphicsPath resultPath = ((operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
  3045. ? new GraphicsPath() : null;
  3046. //************************************************************
  3047. //** Transform marker position in 3D space
  3048. //************************************************************
  3049. // Get projection coordinates
  3050. Point3D[] marker3DPosition = new Point3D[1];
  3051. marker3DPosition[0] = new Point3D(point.X, point.Y, positionZ);
  3052. // Transform coordinates of the marker center
  3053. matrix.TransformPoints(marker3DPosition);
  3054. PointF markerRotatedPosition = marker3DPosition[0].PointF;
  3055. // Translate to absolute coordinates
  3056. markerRotatedPosition = graph.GetAbsolutePoint(markerRotatedPosition);
  3057. //************************************************************
  3058. //** For those markers that do not have a 3D version - draw the same as in 2D
  3059. //************************************************************
  3060. if(markerImage.Length > 0 ||
  3061. !(markerStyle == MarkerStyle.Circle ||
  3062. markerStyle == MarkerStyle.Square) )
  3063. {
  3064. // Call 2D version of the method
  3065. if( (operationType & DrawingOperationTypes.DrawElement) == DrawingOperationTypes.DrawElement)
  3066. {
  3067. graph.DrawMarkerAbs(markerRotatedPosition, markerStyle, markerSize, markerColor, markerBorderColor, markerBorderSize, markerImage, markerImageTransparentColor, shadowSize, shadowColor, imageScaleRect, false);
  3068. }
  3069. // Prepare marker path
  3070. if( (operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
  3071. {
  3072. RectangleF rect = RectangleF.Empty;
  3073. rect.X = markerRotatedPosition.X - ((float)markerSize)/2F;
  3074. rect.Y = markerRotatedPosition.Y - ((float)markerSize)/2F;
  3075. rect.Width = markerSize;
  3076. rect.Height = markerSize;
  3077. resultPath.AddRectangle(rect);
  3078. }
  3079. return resultPath;
  3080. }
  3081. //************************************************************
  3082. //** Draw marker
  3083. //************************************************************
  3084. // Check if marker properties are set
  3085. if (markerStyle != MarkerStyle.None && markerSize > 0 && markerColor != Color.Empty)
  3086. {
  3087. // Create solid color brush
  3088. using (SolidBrush brush = new SolidBrush(markerColor))
  3089. {
  3090. // Calculate marker rectangle
  3091. RectangleF rect = RectangleF.Empty;
  3092. rect.X = markerRotatedPosition.X - ((float)markerSize) / 2F;
  3093. rect.Y = markerRotatedPosition.Y - ((float)markerSize) / 2F;
  3094. rect.Width = markerSize;
  3095. rect.Height = markerSize;
  3096. // Calculate relative marker size
  3097. SizeF markerRelativeSize = graph.GetRelativeSize(new SizeF(markerSize, markerSize));
  3098. // Draw marker depending on style
  3099. switch (markerStyle)
  3100. {
  3101. case (MarkerStyle.Circle):
  3102. {
  3103. if ((operationType & DrawingOperationTypes.DrawElement) == DrawingOperationTypes.DrawElement)
  3104. {
  3105. // Draw marker shadow
  3106. if (shadowSize != 0 && shadowColor != Color.Empty)
  3107. {
  3108. if (!graph.softShadows)
  3109. {
  3110. using (Brush shadowBrush = new SolidBrush((shadowColor.A != 255) ? shadowColor : Color.FromArgb(markerColor.A / 2, shadowColor)))
  3111. {
  3112. RectangleF shadowRect = rect;
  3113. shadowRect.X += shadowSize;
  3114. shadowRect.Y += shadowSize;
  3115. graph.FillEllipse(shadowBrush, shadowRect);
  3116. }
  3117. }
  3118. else
  3119. {
  3120. // Add circle to the graphics path
  3121. using (GraphicsPath path = new GraphicsPath())
  3122. {
  3123. path.AddEllipse(rect.X + shadowSize - 1, rect.Y + shadowSize - 1, rect.Width + 2, rect.Height + 2);
  3124. // Create path brush
  3125. using (PathGradientBrush shadowBrush = new PathGradientBrush(path))
  3126. {
  3127. shadowBrush.CenterColor = shadowColor;
  3128. // Set the color along the entire boundary of the path
  3129. Color[] colors = { Color.Transparent };
  3130. shadowBrush.SurroundColors = colors;
  3131. shadowBrush.CenterPoint = new PointF(markerRotatedPosition.X, markerRotatedPosition.Y);
  3132. // Define brush focus scale
  3133. PointF focusScale = new PointF(1 - 2f * shadowSize / rect.Width, 1 - 2f * shadowSize / rect.Height);
  3134. if (focusScale.X < 0)
  3135. {
  3136. focusScale.X = 0;
  3137. }
  3138. if (focusScale.Y < 0)
  3139. {
  3140. focusScale.Y = 0;
  3141. }
  3142. shadowBrush.FocusScales = focusScale;
  3143. // Draw shadow
  3144. graph.FillPath(shadowBrush, path);
  3145. }
  3146. }
  3147. }
  3148. }
  3149. // Create path gradient brush
  3150. using (GraphicsPath brushPath = new GraphicsPath())
  3151. {
  3152. RectangleF rectLightCenter = new RectangleF(rect.Location, rect.Size);
  3153. rectLightCenter.Inflate(rectLightCenter.Width / 4f, rectLightCenter.Height / 4f);
  3154. brushPath.AddEllipse(rectLightCenter);
  3155. using (PathGradientBrush circleBrush = new PathGradientBrush(brushPath))
  3156. {
  3157. circleBrush.CenterColor = ChartGraphics.GetGradientColor(markerColor, Color.White, 0.85);
  3158. circleBrush.SurroundColors = new Color[] { markerColor };
  3159. // Calculate the center point of the gradient
  3160. Point3D[] centerPoint = new Point3D[] { new Point3D(point.X, point.Y, positionZ + markerRelativeSize.Width) };
  3161. matrix.TransformPoints(centerPoint);
  3162. centerPoint[0].PointF = graph.GetAbsolutePoint(centerPoint[0].PointF);
  3163. circleBrush.CenterPoint = centerPoint[0].PointF;
  3164. // Draw circle (sphere)
  3165. graph.FillEllipse(circleBrush, rect);
  3166. graph.DrawEllipse(new Pen(markerBorderColor, markerBorderSize), rect);
  3167. }
  3168. }
  3169. }
  3170. // Prepare marker path
  3171. if ((operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
  3172. {
  3173. resultPath.AddEllipse(rect);
  3174. }
  3175. break;
  3176. }
  3177. case (MarkerStyle.Square):
  3178. {
  3179. // Calculate marker non-rotated rectangle
  3180. RectangleF rectNonRotated = RectangleF.Empty;
  3181. rectNonRotated.X = point.X - ((float)markerRelativeSize.Width) / 2F;
  3182. rectNonRotated.Y = point.Y - ((float)markerRelativeSize.Height) / 2F;
  3183. rectNonRotated.Width = markerRelativeSize.Width;
  3184. rectNonRotated.Height = markerRelativeSize.Height;
  3185. // Draw 3D bar
  3186. resultPath = this.Fill3DRectangle(
  3187. rectNonRotated,
  3188. positionZ - markerRelativeSize.Width / 2f,
  3189. markerRelativeSize.Width,
  3190. matrix,
  3191. lightStyle,
  3192. markerColor,
  3193. markerBorderColor,
  3194. markerBorderSize,
  3195. ChartDashStyle.Solid,
  3196. operationType);
  3197. break;
  3198. }
  3199. default:
  3200. {
  3201. throw (new InvalidOperationException(SR.ExceptionGraphics3DMarkerStyleUnknown));
  3202. }
  3203. }
  3204. }
  3205. }
  3206. return resultPath;
  3207. }
  3208. #endregion
  3209. #region 3D cube surface visibility methods
  3210. /// <summary>
  3211. /// Returns visible surfaces of the 3D cube.
  3212. /// </summary>
  3213. /// <param name="position">2D rectangle coordinates.</param>
  3214. /// <param name="positionZ">Z coordinate of the back side of the cube.</param>
  3215. /// <param name="depth">Cube depth.</param>
  3216. /// <param name="matrix">Coordinate transformation matrix.</param>
  3217. /// <returns>Visible surfaces.</returns>
  3218. internal SurfaceNames GetVisibleSurfaces(
  3219. RectangleF position,
  3220. float positionZ,
  3221. float depth,
  3222. Matrix3D matrix
  3223. )
  3224. {
  3225. // Check if perspective is used
  3226. if(matrix.Perspective != 0)
  3227. {
  3228. // More sofisticated algorithm must be used for visibility detection.
  3229. return GetVisibleSurfacesWithPerspective(position, positionZ, depth, matrix);
  3230. }
  3231. // Front surface is always visible
  3232. SurfaceNames result = SurfaceNames.Front;
  3233. // Left and Right surfaces depend on the Y axis angle
  3234. if (matrix.AngleY > 0)
  3235. {
  3236. result |= SurfaceNames.Right;
  3237. }
  3238. else if (matrix.AngleY < 0)
  3239. {
  3240. result |= SurfaceNames.Left;
  3241. }
  3242. // Top and Bottom surfaces depend on the X axis angle
  3243. if (matrix.AngleX > 0)
  3244. {
  3245. result |= SurfaceNames.Top;
  3246. }
  3247. else if (matrix.AngleX < 0)
  3248. {
  3249. result |= SurfaceNames.Bottom;
  3250. }
  3251. return result;
  3252. }
  3253. /// <summary>
  3254. /// Returns visible surfaces of the 3D cube.
  3255. /// This method takes in consideration the perspective.
  3256. /// </summary>
  3257. /// <param name="position">2D rectangle coordinates.</param>
  3258. /// <param name="positionZ">Z coordinate of the back side of the cube.</param>
  3259. /// <param name="depth">Cube depth.</param>
  3260. /// <param name="matrix">Coordinate transformation matrix.</param>
  3261. /// <returns>Visible surfaces.</returns>
  3262. internal SurfaceNames GetVisibleSurfacesWithPerspective(
  3263. RectangleF position,
  3264. float positionZ,
  3265. float depth,
  3266. Matrix3D matrix)
  3267. {
  3268. // Create cube coordinates in 3D space
  3269. Point3D[] cubePoints = new Point3D[8];
  3270. // Front Side
  3271. cubePoints[0] = new Point3D( position.X, position.Y, positionZ + depth );
  3272. cubePoints[1] = new Point3D( position.X, position.Bottom, positionZ + depth );
  3273. cubePoints[2] = new Point3D( position.Right, position.Bottom, positionZ + depth );
  3274. cubePoints[3] = new Point3D( position.Right, position.Y, positionZ + depth );
  3275. // Back Side
  3276. cubePoints[4] = new Point3D( position.X, position.Y, positionZ );
  3277. cubePoints[5] = new Point3D( position.X, position.Bottom, positionZ );
  3278. cubePoints[6] = new Point3D( position.Right, position.Bottom, positionZ );
  3279. cubePoints[7] = new Point3D( position.Right, position.Y, positionZ );
  3280. // Tranform coordinates
  3281. matrix.TransformPoints( cubePoints );
  3282. // Detect surfaces visibility
  3283. return GetVisibleSurfacesWithPerspective(cubePoints);
  3284. }
  3285. /// <summary>
  3286. /// Returns visible surfaces of the 3D cube.
  3287. /// This method takes in consideration the perspective.
  3288. /// </summary>
  3289. /// <param name="cubePoints">Array of 8 points which define the cube.</param>
  3290. /// <returns>Visible surfaces.</returns>
  3291. internal SurfaceNames GetVisibleSurfacesWithPerspective(Point3D[] cubePoints)
  3292. {
  3293. // Check imput array size
  3294. if(cubePoints.Length != 8)
  3295. {
  3296. throw (new ArgumentException(SR.ExceptionGraphics3DCoordinatesInvalid, "cubePoints"));
  3297. }
  3298. // Detect surfaces visibility
  3299. SurfaceNames result = 0;
  3300. // Check the front side
  3301. if(IsSurfaceVisible(cubePoints[0],cubePoints[3],cubePoints[2]))
  3302. {
  3303. result |= SurfaceNames.Front;
  3304. }
  3305. // Check the back side
  3306. if(IsSurfaceVisible(cubePoints[4],cubePoints[5],cubePoints[6]))
  3307. {
  3308. result |= SurfaceNames.Back;
  3309. }
  3310. // Check the left side
  3311. if(IsSurfaceVisible(cubePoints[0],cubePoints[1],cubePoints[5]))
  3312. {
  3313. result |= SurfaceNames.Left;
  3314. }
  3315. // Check the right side
  3316. if(IsSurfaceVisible(cubePoints[3],cubePoints[7],cubePoints[6]))
  3317. {
  3318. result |= SurfaceNames.Right;
  3319. }
  3320. // Check the top side
  3321. if(IsSurfaceVisible(cubePoints[4],cubePoints[7],cubePoints[3]))
  3322. {
  3323. result |= SurfaceNames.Top;
  3324. }
  3325. // Check the bottom side
  3326. if(IsSurfaceVisible(cubePoints[1],cubePoints[2],cubePoints[6]))
  3327. {
  3328. result |= SurfaceNames.Bottom;
  3329. }
  3330. return result;
  3331. }
  3332. /// <summary>
  3333. /// Checks surface visibility using 3 points and clockwise points index rotation.
  3334. /// </summary>
  3335. /// <param name="first">First point.</param>
  3336. /// <param name="second">Second point.</param>
  3337. /// <param name="tree">Third point.</param>
  3338. /// <returns>True if surface is visible</returns>
  3339. internal static bool IsSurfaceVisible( Point3D first, Point3D second, Point3D tree )
  3340. {
  3341. // Check if points are oriented clocwise in 2D projection.
  3342. // If points are clockwise the surface is visible.
  3343. float a = ( first.Y - second.Y ) / ( first.X - second.X );
  3344. float b = first.Y - a * first.X;
  3345. if( first.X == second.X )
  3346. {
  3347. if( first.Y > second.Y )
  3348. {
  3349. if( tree.X > first.X )
  3350. {
  3351. return true;
  3352. }
  3353. else
  3354. {
  3355. return false;
  3356. }
  3357. }
  3358. else
  3359. {
  3360. if( tree.X > first.X )
  3361. {
  3362. return false;
  3363. }
  3364. else
  3365. {
  3366. return true;
  3367. }
  3368. }
  3369. }
  3370. else if ( first.X < second.X )
  3371. {
  3372. if( tree.Y < a * tree.X + b )
  3373. {
  3374. return false;
  3375. }
  3376. else
  3377. {
  3378. return true;
  3379. }
  3380. }
  3381. else
  3382. {
  3383. if( tree.Y <= a * tree.X + b )
  3384. {
  3385. return true;
  3386. }
  3387. else
  3388. {
  3389. return false;
  3390. }
  3391. }
  3392. }
  3393. #endregion
  3394. #region Line intersection helper method
  3395. /// <summary>
  3396. /// Gets intersection point of two lines
  3397. /// </summary>
  3398. /// <param name="x1">First X value of first line.</param>
  3399. /// <param name="y1">First Y value of first line.</param>
  3400. /// <param name="x2">Second X value of first line.</param>
  3401. /// <param name="y2">Second Y value of first line.</param>
  3402. /// <param name="x3">First X value of second line.</param>
  3403. /// <param name="y3">First Y value of second line.</param>
  3404. /// <param name="x4">Second X value of second line.</param>
  3405. /// <param name="y4">Second Y value of second line.</param>
  3406. /// <returns>Intersection coordinates.</returns>
  3407. internal static PointF GetLinesIntersection(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4)
  3408. {
  3409. PointF result = PointF.Empty;
  3410. // Special case for horizontal & vertical lines
  3411. if(x1 == x2 && y3 == y4)
  3412. {
  3413. result.X = x1;
  3414. result.Y = y3;
  3415. return result;
  3416. }
  3417. else if(y1 == y2 && x3 == x4)
  3418. {
  3419. result.X = x3;
  3420. result.Y = y1;
  3421. return result;
  3422. }
  3423. else if(x1 == x2)
  3424. {
  3425. result.X = x1;
  3426. result.Y = (result.X - x3) * (y4 - y3);
  3427. result.Y /= x4 - x3;
  3428. result.Y += y3;
  3429. return result;
  3430. }
  3431. else if(x3 == x4)
  3432. {
  3433. result.X = x3;
  3434. result.Y = (result.X - x1) * (y2 - y1);
  3435. result.Y /= x2 - x1;
  3436. result.Y += y1;
  3437. return result;
  3438. }
  3439. // Calculate line eqaution
  3440. float a1 = ( y1 - y2 ) / ( x1 - x2 );
  3441. float b1 = y1 - a1 * x1;
  3442. float a2 = ( y3 - y4 ) / ( x3 - x4 );
  3443. float b2 = y3 - a2 * x3;
  3444. // Calculate intersection point
  3445. result.X = (b2 - b1)/(a1 - a2);
  3446. result.Y = a1*result.X + b1;
  3447. return result;
  3448. }
  3449. #endregion
  3450. #region 3D Cylinder drawing methods
  3451. /// <summary>
  3452. /// Function is used to calculate the coordinates of the 2D rectangle in 3D space
  3453. /// and either draw it or/and calculate the bounding path for selection.
  3454. /// </summary>
  3455. /// <param name="position">Position of 2D rectangle.</param>
  3456. /// <param name="positionZ">Z position of the back side of the 3D rectangle.</param>
  3457. /// <param name="depth">Depth of the 3D rectangle.</param>
  3458. /// <param name="matrix">Coordinate transformation matrix.</param>
  3459. /// <param name="lightStyle">LightStyle style (None, Simplistic, Realistic).</param>
  3460. /// <param name="backColor">Color of rectangle</param>
  3461. /// <param name="topRightDarkening">Top (or right in bar chart) darkening effect.</param>
  3462. /// <param name="bottomLeftDarkening">Bottom (or left in bar chart) darkening effect.</param>
  3463. /// <param name="borderColor">Border Color</param>
  3464. /// <param name="borderWidth">Border Width</param>
  3465. /// <param name="borderDashStyle">Border Style</param>
  3466. /// <param name="veticalOrientation">Defines if bar is vertical or horizontal.</param>
  3467. /// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
  3468. /// <returns>Returns elemnt shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
  3469. internal GraphicsPath Fill3DRectangleAsCylinder(
  3470. RectangleF position,
  3471. float positionZ,
  3472. float depth,
  3473. Matrix3D matrix,
  3474. LightStyle lightStyle,
  3475. Color backColor,
  3476. float topRightDarkening,
  3477. float bottomLeftDarkening,
  3478. Color borderColor,
  3479. int borderWidth,
  3480. ChartDashStyle borderDashStyle,
  3481. bool veticalOrientation,
  3482. DrawingOperationTypes operationType)
  3483. {
  3484. Point3D[] cubePoints = new Point3D[8];
  3485. GraphicsPath resultPath = ((operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
  3486. ? new GraphicsPath() : null;
  3487. //*******************************************************
  3488. //** Define coordinates to draw the cylinder
  3489. //*******************************************************
  3490. if(veticalOrientation)
  3491. {
  3492. cubePoints[0] = new Point3D( position.X, position.Y, positionZ + depth / 2f );
  3493. cubePoints[1] = new Point3D( position.X, position.Bottom, positionZ + depth / 2f );
  3494. cubePoints[2] = new Point3D( position.Right, position.Bottom, positionZ + depth / 2f );
  3495. cubePoints[3] = new Point3D( position.Right, position.Y, positionZ + depth / 2f );
  3496. float middleXValue = position.X + position.Width / 2f;
  3497. cubePoints[4] = new Point3D( middleXValue, position.Y, positionZ + depth );
  3498. cubePoints[5] = new Point3D( middleXValue, position.Bottom, positionZ + depth );
  3499. cubePoints[6] = new Point3D( middleXValue, position.Bottom, positionZ );
  3500. cubePoints[7] = new Point3D( middleXValue, position.Y, positionZ );
  3501. }
  3502. else
  3503. {
  3504. cubePoints[0] = new Point3D( position.Right, position.Y, positionZ + depth / 2f );
  3505. cubePoints[1] = new Point3D( position.X, position.Y, positionZ + depth / 2f );
  3506. cubePoints[2] = new Point3D( position.X, position.Bottom, positionZ + depth / 2f );
  3507. cubePoints[3] = new Point3D( position.Right, position.Bottom, positionZ + depth / 2f );
  3508. float middleYValue = position.Y + position.Height / 2f;
  3509. cubePoints[4] = new Point3D( position.Right, middleYValue, positionZ + depth );
  3510. cubePoints[5] = new Point3D( position.X, middleYValue, positionZ + depth );
  3511. cubePoints[6] = new Point3D( position.X, middleYValue, positionZ );
  3512. cubePoints[7] = new Point3D( position.Right, middleYValue, positionZ );
  3513. }
  3514. // Tranform cylinder coordinates
  3515. matrix.TransformPoints( cubePoints );
  3516. // Covert coordinates to absolute
  3517. for(int pointIndex = 0; pointIndex < cubePoints.Length; pointIndex++)
  3518. {
  3519. cubePoints[pointIndex].PointF = GetAbsolutePoint(cubePoints[pointIndex].PointF);
  3520. }
  3521. //*******************************************************
  3522. //** Get cylinder colors.
  3523. //*******************************************************
  3524. if( lightStyle == LightStyle.None &&
  3525. (borderWidth == 0 || borderDashStyle == ChartDashStyle.NotSet || borderColor == Color.Empty) )
  3526. {
  3527. borderColor = ChartGraphics.GetGradientColor( backColor, Color.Black, 0.5 );
  3528. }
  3529. // Get surface colors
  3530. Color frontLightColor, leftLightColor, topLightColor, backLightColor, rightLightColor, bottomLightColor;
  3531. matrix.GetLight( backColor, out frontLightColor, out backLightColor, out leftLightColor, out rightLightColor, out topLightColor, out bottomLightColor );
  3532. // Darken colors by specified values
  3533. if(topRightDarkening != 0f)
  3534. {
  3535. if(veticalOrientation)
  3536. {
  3537. topLightColor = ChartGraphics.GetGradientColor(topLightColor, Color.Black, topRightDarkening);
  3538. }
  3539. else
  3540. {
  3541. rightLightColor = ChartGraphics.GetGradientColor(rightLightColor, Color.Black, topRightDarkening);
  3542. }
  3543. }
  3544. if(bottomLeftDarkening != 0f)
  3545. {
  3546. if(veticalOrientation)
  3547. {
  3548. bottomLightColor = ChartGraphics.GetGradientColor(bottomLightColor, Color.Black, bottomLeftDarkening);
  3549. }
  3550. else
  3551. {
  3552. leftLightColor = ChartGraphics.GetGradientColor(leftLightColor, Color.Black, bottomLeftDarkening);
  3553. }
  3554. }
  3555. //*******************************************************
  3556. //** Check visible surfaces
  3557. //*******************************************************
  3558. SurfaceNames visibleSurfaces = GetVisibleSurfacesWithPerspective(position,positionZ,depth,matrix);
  3559. // Front surface is always visible in cylinder
  3560. if( (visibleSurfaces & SurfaceNames.Front) != SurfaceNames.Front)
  3561. {
  3562. visibleSurfaces |= SurfaceNames.Front;
  3563. }
  3564. //*******************************************************
  3565. //** Create flattened paths for the sides of the
  3566. //** cylinder (top,bottom/left,rigth)
  3567. //*******************************************************
  3568. PointF[] sidePoints = new PointF[4];
  3569. sidePoints[0] = cubePoints[6].PointF;
  3570. sidePoints[1] = cubePoints[1].PointF;
  3571. sidePoints[2] = cubePoints[5].PointF;
  3572. sidePoints[3] = cubePoints[2].PointF;
  3573. GraphicsPath bottomLeftSide = new GraphicsPath();
  3574. bottomLeftSide.AddClosedCurve(sidePoints, 0.8f);
  3575. bottomLeftSide.Flatten();
  3576. sidePoints[0] = cubePoints[7].PointF;
  3577. sidePoints[1] = cubePoints[0].PointF;
  3578. sidePoints[2] = cubePoints[4].PointF;
  3579. sidePoints[3] = cubePoints[3].PointF;
  3580. GraphicsPath topRigthSide = new GraphicsPath();
  3581. topRigthSide.AddClosedCurve(sidePoints, 0.8f);
  3582. topRigthSide.Flatten();
  3583. //*******************************************************
  3584. //** Find cylinder angle
  3585. //*******************************************************
  3586. float cylinderAngle = 90f;
  3587. if(cubePoints[5].PointF.Y != cubePoints[4].PointF.Y)
  3588. {
  3589. cylinderAngle = (float)Math.Atan(
  3590. (cubePoints[4].PointF.X - cubePoints[5].PointF.X) /
  3591. (cubePoints[5].PointF.Y - cubePoints[4].PointF.Y) );
  3592. cylinderAngle = (float)Math.Round(cylinderAngle * 180f / (float)Math.PI);
  3593. }
  3594. //*******************************************************
  3595. //** Draw all invisible surfaces first (if semi-transparent color is used)
  3596. //*******************************************************
  3597. for(int drawVisible = 0; drawVisible <= 1; drawVisible++)
  3598. {
  3599. // Do not draw invisible surfaces for solid colors
  3600. if(drawVisible == 0 && backColor.A == 255)
  3601. {
  3602. continue;
  3603. }
  3604. // Check visibility of all surfaces and draw them
  3605. for(int surfaceIndex = (int)SurfaceNames.Front; surfaceIndex <= (int)SurfaceNames.Bottom; surfaceIndex *= 2)
  3606. {
  3607. SurfaceNames currentSurface = (SurfaceNames)surfaceIndex;
  3608. // Check if surface is visible or semi-transparent color is used
  3609. bool isVisible = (visibleSurfaces & currentSurface) != 0;
  3610. if(isVisible && drawVisible == 1 ||
  3611. !isVisible && drawVisible == 0)
  3612. {
  3613. // Fill surface coordinates and color
  3614. GraphicsPath pathToDraw = null;
  3615. Color surfaceColor = backColor;
  3616. // Declare a special brush for the front surface
  3617. Brush frontSurfaceBrush = null;
  3618. switch(currentSurface)
  3619. {
  3620. case(SurfaceNames.Front):
  3621. {
  3622. // Set front surface color
  3623. surfaceColor = backColor;
  3624. // Add ellipse segment of the cylinder on top/rigth (reversed)
  3625. pathToDraw = new GraphicsPath();
  3626. PointF leftSideLinePoint = PointF.Empty;
  3627. PointF rightSideLinePoint = PointF.Empty;
  3628. AddEllipseSegment(
  3629. pathToDraw,
  3630. topRigthSide,
  3631. bottomLeftSide,
  3632. (matrix.Perspective == 0) ? veticalOrientation : false,
  3633. cylinderAngle,
  3634. out leftSideLinePoint,
  3635. out rightSideLinePoint);
  3636. pathToDraw.Reverse();
  3637. // Add ellipse segment of the cylinder on bottom/left
  3638. PointF leftOppSideLinePoint = PointF.Empty;
  3639. PointF rightOppSideLinePoint = PointF.Empty;
  3640. AddEllipseSegment(
  3641. pathToDraw,
  3642. bottomLeftSide,
  3643. topRigthSide,
  3644. (matrix.Perspective == 0) ? veticalOrientation : false,
  3645. cylinderAngle,
  3646. out leftOppSideLinePoint,
  3647. out rightOppSideLinePoint);
  3648. pathToDraw.CloseAllFigures();
  3649. // Reset indexes of opposite side points
  3650. this._oppLeftBottomPoint = -1;
  3651. this._oppRigthTopPoint = -1;
  3652. // Create gradient brush for the front surface
  3653. if(lightStyle != LightStyle.None)
  3654. {
  3655. RectangleF boundsRect = pathToDraw.GetBounds();
  3656. if(boundsRect.Height > 0 && boundsRect.Width > 0)
  3657. {
  3658. Color lightColor = ChartGraphics.GetGradientColor( backColor, Color.White, 0.3 );
  3659. Color darkColor = ChartGraphics.GetGradientColor( backColor, Color.Black, 0.3 );
  3660. // Create gradient
  3661. if(!leftSideLinePoint.IsEmpty &&
  3662. !rightSideLinePoint.IsEmpty &&
  3663. !leftOppSideLinePoint.IsEmpty &&
  3664. !rightOppSideLinePoint.IsEmpty)
  3665. {
  3666. PointF boundsRectMiddlePoint = PointF.Empty;
  3667. boundsRectMiddlePoint.X = boundsRect.X + boundsRect.Width/2f;
  3668. boundsRectMiddlePoint.Y = boundsRect.Y + boundsRect.Height/2f;
  3669. PointF centralLinePoint = PointF.Empty;
  3670. double centralLineAngle = ((cylinderAngle) * Math.PI / 180f);
  3671. if(cylinderAngle == 0 || cylinderAngle == 180 || cylinderAngle == -180)
  3672. {
  3673. centralLinePoint.X = boundsRectMiddlePoint.X + 100f;
  3674. centralLinePoint.Y = boundsRectMiddlePoint.Y;
  3675. }
  3676. else if(cylinderAngle == 90 || cylinderAngle == -90)
  3677. {
  3678. centralLinePoint.X = boundsRectMiddlePoint.X;
  3679. centralLinePoint.Y = boundsRectMiddlePoint.Y + 100f;
  3680. }
  3681. else if(cylinderAngle > -45 && cylinderAngle < 45)
  3682. {
  3683. centralLinePoint.X = boundsRectMiddlePoint.X + 100f;
  3684. centralLinePoint.Y = (float)(Math.Tan(centralLineAngle) * centralLinePoint.X);
  3685. centralLinePoint.Y += (float)(boundsRectMiddlePoint.Y - Math.Tan(centralLineAngle) * boundsRectMiddlePoint.X);
  3686. }
  3687. else
  3688. {
  3689. centralLinePoint.Y = boundsRectMiddlePoint.Y + 100f;
  3690. centralLinePoint.X = (float)(centralLinePoint.Y - (boundsRectMiddlePoint.Y - Math.Tan(centralLineAngle) * boundsRectMiddlePoint.X));
  3691. centralLinePoint.X /= (float)(Math.Tan(centralLineAngle));
  3692. }
  3693. PointF middlePoint1 = ChartGraphics.GetLinesIntersection(
  3694. boundsRectMiddlePoint.X, boundsRectMiddlePoint.Y,
  3695. centralLinePoint.X, centralLinePoint.Y,
  3696. leftSideLinePoint.X, leftSideLinePoint.Y,
  3697. leftOppSideLinePoint.X, leftOppSideLinePoint.Y);
  3698. PointF middlePoint2 = ChartGraphics.GetLinesIntersection(
  3699. boundsRectMiddlePoint.X, boundsRectMiddlePoint.Y,
  3700. centralLinePoint.X, centralLinePoint.Y,
  3701. rightSideLinePoint.X, rightSideLinePoint.Y,
  3702. rightOppSideLinePoint.X, rightOppSideLinePoint.Y);
  3703. // Gradient points can not have same coordinates
  3704. if(middlePoint1.X != middlePoint2.X || middlePoint1.Y != middlePoint2.Y)
  3705. {
  3706. frontSurfaceBrush = new LinearGradientBrush(
  3707. middlePoint1,
  3708. middlePoint2,
  3709. lightColor,
  3710. darkColor);
  3711. ColorBlend colorBlend = new ColorBlend(5);
  3712. colorBlend.Colors[0] = darkColor;
  3713. colorBlend.Colors[1] = darkColor;
  3714. colorBlend.Colors[2] = lightColor;
  3715. colorBlend.Colors[3] = darkColor;
  3716. colorBlend.Colors[4] = darkColor;
  3717. colorBlend.Positions[0] = 0.0f;
  3718. colorBlend.Positions[1] = 0.0f;
  3719. colorBlend.Positions[2] = 0.5f;
  3720. colorBlend.Positions[3] = 1.0f;
  3721. colorBlend.Positions[4] = 1.0f;
  3722. ((LinearGradientBrush)frontSurfaceBrush).InterpolationColors = colorBlend;
  3723. }
  3724. }
  3725. }
  3726. }
  3727. break;
  3728. }
  3729. case(SurfaceNames.Top):
  3730. if(veticalOrientation)
  3731. {
  3732. surfaceColor = topLightColor;
  3733. pathToDraw = topRigthSide;
  3734. }
  3735. break;
  3736. case(SurfaceNames.Bottom):
  3737. if(veticalOrientation)
  3738. {
  3739. surfaceColor = bottomLightColor;
  3740. pathToDraw = bottomLeftSide;
  3741. }
  3742. break;
  3743. case(SurfaceNames.Right):
  3744. if(!veticalOrientation)
  3745. {
  3746. surfaceColor = rightLightColor;
  3747. pathToDraw = topRigthSide;
  3748. }
  3749. break;
  3750. case(SurfaceNames.Left):
  3751. if(!veticalOrientation)
  3752. {
  3753. surfaceColor = leftLightColor;
  3754. pathToDraw = bottomLeftSide;
  3755. }
  3756. break;
  3757. }
  3758. //*******************************************************
  3759. //** Draw surface
  3760. //*******************************************************
  3761. if(pathToDraw != null)
  3762. {
  3763. if( (operationType & DrawingOperationTypes.DrawElement) == DrawingOperationTypes.DrawElement)
  3764. {
  3765. // Draw only completly visible surfaces
  3766. if((visibleSurfaces & currentSurface) != 0)
  3767. {
  3768. using (Brush brush = new SolidBrush(surfaceColor))
  3769. {
  3770. FillPath( (frontSurfaceBrush == null) ? brush : frontSurfaceBrush, pathToDraw );
  3771. }
  3772. }
  3773. // Draw surface border
  3774. using (Pen pen = new Pen(borderColor, borderWidth))
  3775. {
  3776. pen.DashStyle = GetPenStyle(borderDashStyle);
  3777. if (lightStyle != LightStyle.None &&
  3778. (borderWidth == 0 || borderDashStyle == ChartDashStyle.NotSet || borderColor == Color.Empty))
  3779. {
  3780. // Draw line of the darker color inside the cylinder
  3781. pen.Color = frontSurfaceBrush == null ? surfaceColor : ChartGraphics.GetGradientColor(backColor, Color.Black, 0.3);
  3782. pen.Width = 1;
  3783. pen.Alignment = PenAlignment.Inset;
  3784. }
  3785. pen.StartCap = LineCap.Round;
  3786. pen.EndCap = LineCap.Round;
  3787. pen.LineJoin = LineJoin.Bevel;
  3788. DrawPath(pen, pathToDraw);
  3789. }
  3790. }
  3791. // Add surface coordinate to the path
  3792. if( (operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
  3793. {
  3794. // Only if surface is completly visible
  3795. if((visibleSurfaces & currentSurface) != 0)
  3796. {
  3797. if(pathToDraw != null && pathToDraw.PointCount > 0)
  3798. {
  3799. resultPath.AddPath(pathToDraw, true);
  3800. resultPath.SetMarkers();
  3801. }
  3802. }
  3803. }
  3804. }
  3805. }
  3806. }
  3807. }
  3808. return resultPath;
  3809. }
  3810. /// <summary>
  3811. /// Adds segment of the ellipse to form the front surface of the cylinder
  3812. /// </summary>
  3813. internal void AddEllipseSegment(
  3814. GraphicsPath resultPath,
  3815. GraphicsPath ellipseFlattenPath,
  3816. GraphicsPath oppositeEllipseFlattenPath,
  3817. bool veticalOrientation,
  3818. float cylinderAngle,
  3819. out PointF leftSideLinePoint,
  3820. out PointF rightSideLinePoint)
  3821. {
  3822. // Initialize return values
  3823. leftSideLinePoint = PointF.Empty;
  3824. rightSideLinePoint = PointF.Empty;
  3825. // Check if input path is empty
  3826. if(ellipseFlattenPath.PointCount == 0)
  3827. {
  3828. return;
  3829. }
  3830. // Find the index the left/bottom most and right/top most point in flatten array of ellipse points
  3831. int leftBottomPoint = 0;
  3832. int rigthTopPoint = 0;
  3833. PointF[] ellipsePoints = ellipseFlattenPath.PathPoints;
  3834. if(veticalOrientation)
  3835. {
  3836. for(int pointIndex = 1; pointIndex < ellipsePoints.Length; pointIndex++)
  3837. {
  3838. if(ellipsePoints[leftBottomPoint].X > ellipsePoints[pointIndex].X)
  3839. {
  3840. leftBottomPoint = pointIndex;
  3841. }
  3842. if(ellipsePoints[rigthTopPoint].X < ellipsePoints[pointIndex].X)
  3843. {
  3844. rigthTopPoint = pointIndex;
  3845. }
  3846. }
  3847. }
  3848. else
  3849. {
  3850. bool doneFlag = false;
  3851. leftBottomPoint = -1;
  3852. rigthTopPoint = -1;
  3853. if(this._oppLeftBottomPoint != -1 && this._oppRigthTopPoint != -1)
  3854. {
  3855. // Get index from previously calculated values
  3856. leftBottomPoint = this._oppLeftBottomPoint;
  3857. rigthTopPoint = this._oppRigthTopPoint;
  3858. }
  3859. else
  3860. {
  3861. // Loop through first ellipse points
  3862. PointF[] oppositeEllipsePoints = oppositeEllipseFlattenPath.PathPoints;
  3863. for(int pointIndex = 0; !doneFlag && pointIndex < ellipsePoints.Length; pointIndex++)
  3864. {
  3865. // Loop through opposite ellipse points
  3866. for(int pointOppositeIndex = 0; !doneFlag && pointOppositeIndex < oppositeEllipsePoints.Length; pointOppositeIndex++)
  3867. {
  3868. bool closeToVertical = false;
  3869. bool pointsOnLeft = false;
  3870. bool pointsOnRight = false;
  3871. //if(cylinderAngle == 0 || cylinderAngle == 180 || cylinderAngle == -180)
  3872. if(cylinderAngle > -30 && cylinderAngle < 30)
  3873. {
  3874. closeToVertical = true;
  3875. }
  3876. if(closeToVertical)
  3877. {
  3878. if(oppositeEllipsePoints[pointOppositeIndex].Y == ellipsePoints[pointIndex].Y)
  3879. {
  3880. continue;
  3881. }
  3882. float linePointX = oppositeEllipsePoints[pointOppositeIndex].X - ellipsePoints[pointIndex].X;
  3883. linePointX /= oppositeEllipsePoints[pointOppositeIndex].Y - ellipsePoints[pointIndex].Y;
  3884. // Check if this line has any points to the right/left
  3885. for(int innerPointIndex = 0; innerPointIndex < ellipsePoints.Length; innerPointIndex++)
  3886. {
  3887. // Skip points used to define line function
  3888. if(innerPointIndex == pointIndex)
  3889. {
  3890. continue;
  3891. }
  3892. float x = linePointX;
  3893. x *= ellipsePoints[innerPointIndex].Y - ellipsePoints[pointIndex].Y;
  3894. x += ellipsePoints[pointIndex].X;
  3895. if(x > ellipsePoints[innerPointIndex].X)
  3896. {
  3897. pointsOnLeft = true;
  3898. }
  3899. if(x < ellipsePoints[innerPointIndex].X)
  3900. {
  3901. pointsOnRight = true;
  3902. }
  3903. if(pointsOnLeft && pointsOnRight)
  3904. {
  3905. break;
  3906. }
  3907. }
  3908. if(pointsOnLeft == false || pointsOnRight == false)
  3909. {
  3910. for(int innerPointIndex = 0; innerPointIndex < oppositeEllipsePoints.Length; innerPointIndex++)
  3911. {
  3912. // Skip points used to define line function
  3913. if(innerPointIndex == pointOppositeIndex)
  3914. {
  3915. continue;
  3916. }
  3917. float x = linePointX;
  3918. x *= oppositeEllipsePoints[innerPointIndex].Y - ellipsePoints[pointIndex].Y;
  3919. x += ellipsePoints[pointIndex].X;
  3920. if(x > oppositeEllipsePoints[innerPointIndex].X)
  3921. {
  3922. pointsOnLeft = true;
  3923. }
  3924. if(x < oppositeEllipsePoints[innerPointIndex].X)
  3925. {
  3926. pointsOnRight = true;
  3927. }
  3928. if(pointsOnLeft && pointsOnRight)
  3929. {
  3930. break;
  3931. }
  3932. }
  3933. }
  3934. }
  3935. else
  3936. {
  3937. if(oppositeEllipsePoints[pointOppositeIndex].X == ellipsePoints[pointIndex].X)
  3938. {
  3939. continue;
  3940. }
  3941. float linePointY = oppositeEllipsePoints[pointOppositeIndex].Y - ellipsePoints[pointIndex].Y;
  3942. linePointY /= oppositeEllipsePoints[pointOppositeIndex].X - ellipsePoints[pointIndex].X;
  3943. // Check if this line has any points to the right/left
  3944. for(int innerPointIndex = 0; innerPointIndex < ellipsePoints.Length; innerPointIndex++)
  3945. {
  3946. // Skip points used to define line function
  3947. if(innerPointIndex == pointIndex)
  3948. {
  3949. continue;
  3950. }
  3951. float y = linePointY;
  3952. y *= ellipsePoints[innerPointIndex].X - ellipsePoints[pointIndex].X;
  3953. y += ellipsePoints[pointIndex].Y;
  3954. if(y > ellipsePoints[innerPointIndex].Y)
  3955. {
  3956. pointsOnLeft = true;
  3957. }
  3958. if(y < ellipsePoints[innerPointIndex].Y)
  3959. {
  3960. pointsOnRight = true;
  3961. }
  3962. if(pointsOnLeft && pointsOnRight)
  3963. {
  3964. break;
  3965. }
  3966. }
  3967. if(pointsOnLeft == false || pointsOnRight == false)
  3968. {
  3969. for(int innerPointIndex = 0; innerPointIndex < oppositeEllipsePoints.Length; innerPointIndex++)
  3970. {
  3971. // Skip points used to define line function
  3972. if(innerPointIndex == pointOppositeIndex)
  3973. {
  3974. continue;
  3975. }
  3976. float y = linePointY;
  3977. y *= oppositeEllipsePoints[innerPointIndex].X - ellipsePoints[pointIndex].X;
  3978. y += ellipsePoints[pointIndex].Y;
  3979. if(y > oppositeEllipsePoints[innerPointIndex].Y)
  3980. {
  3981. pointsOnLeft = true;
  3982. }
  3983. if(y < oppositeEllipsePoints[innerPointIndex].Y)
  3984. {
  3985. pointsOnRight = true;
  3986. }
  3987. if(pointsOnLeft && pointsOnRight)
  3988. {
  3989. break;
  3990. }
  3991. }
  3992. }
  3993. }
  3994. if(!pointsOnLeft && leftBottomPoint == -1)
  3995. {
  3996. leftBottomPoint = pointIndex;
  3997. this._oppLeftBottomPoint = pointOppositeIndex;
  3998. }
  3999. if(!pointsOnRight && rigthTopPoint == -1)
  4000. {
  4001. rigthTopPoint = pointIndex;
  4002. this._oppRigthTopPoint = pointOppositeIndex;
  4003. }
  4004. if(leftBottomPoint >= 0 && rigthTopPoint >= 0)
  4005. {
  4006. doneFlag = true;
  4007. if(closeToVertical)
  4008. {
  4009. if(ellipsePoints[leftBottomPoint].Y > oppositeEllipsePoints[this._oppLeftBottomPoint].Y)
  4010. {
  4011. int temp = leftBottomPoint;
  4012. leftBottomPoint = rigthTopPoint;
  4013. rigthTopPoint = temp;
  4014. temp = this._oppLeftBottomPoint;
  4015. this._oppLeftBottomPoint = this._oppRigthTopPoint;
  4016. this._oppRigthTopPoint = temp;
  4017. }
  4018. }
  4019. }
  4020. }
  4021. }
  4022. }
  4023. }
  4024. // Point indexes were not found
  4025. if(leftBottomPoint == rigthTopPoint ||
  4026. rigthTopPoint == -1 ||
  4027. leftBottomPoint == -1)
  4028. {
  4029. return;
  4030. }
  4031. // Set left\right line coordinates
  4032. leftSideLinePoint = ellipsePoints[leftBottomPoint];
  4033. rightSideLinePoint = ellipsePoints[rigthTopPoint];
  4034. // Add required ellipse segment to the result path
  4035. for(int pointIndex = leftBottomPoint + 1; pointIndex != rigthTopPoint + 1; pointIndex++)
  4036. {
  4037. if(pointIndex > ellipsePoints.Length - 1)
  4038. {
  4039. resultPath.AddLine(ellipsePoints[ellipsePoints.Length - 1], ellipsePoints[0]);
  4040. pointIndex = 0;
  4041. continue;
  4042. }
  4043. resultPath.AddLine(ellipsePoints[pointIndex - 1], ellipsePoints[pointIndex]);
  4044. }
  4045. }
  4046. #endregion
  4047. }
  4048. /// <summary>
  4049. /// The Point3D class represents point coordinates in 3D space.
  4050. /// </summary>
  4051. public class Point3D
  4052. {
  4053. #region Fields
  4054. // Point X and Y coordinates
  4055. private PointF _coordXY = new PointF(0f, 0f);
  4056. // Point Z coordinate (depth)
  4057. private float _coordZ = 0;
  4058. #endregion
  4059. #region Properties
  4060. /// <summary>
  4061. /// Gets or sets the X coordinate of the point.
  4062. /// </summary>
  4063. [
  4064. Bindable(true),
  4065. DefaultValue(0),
  4066. SRDescription("DescriptionAttributePoint3D_X")
  4067. ]
  4068. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "X")]
  4069. public float X
  4070. {
  4071. get
  4072. {
  4073. return this._coordXY.X;
  4074. }
  4075. set
  4076. {
  4077. this._coordXY.X = value;
  4078. }
  4079. }
  4080. /// <summary>
  4081. /// Gets or sets the Y coordinate of the point.
  4082. /// </summary>
  4083. [
  4084. Bindable(true),
  4085. DefaultValue(0),
  4086. SRDescription("DescriptionAttributePoint3D_Y")
  4087. ]
  4088. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Y")]
  4089. public float Y
  4090. {
  4091. get
  4092. {
  4093. return this._coordXY.Y;
  4094. }
  4095. set
  4096. {
  4097. this._coordXY.Y = value;
  4098. }
  4099. }
  4100. /// <summary>
  4101. /// Gets or sets the Z coordinate of the point.
  4102. /// </summary>
  4103. [
  4104. Bindable(true),
  4105. DefaultValue(0),
  4106. SRDescription("DescriptionAttributePoint3D_Z")
  4107. ]
  4108. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Z")]
  4109. public float Z
  4110. {
  4111. get
  4112. {
  4113. return this._coordZ;
  4114. }
  4115. set
  4116. {
  4117. this._coordZ = value;
  4118. }
  4119. }
  4120. /// <summary>
  4121. /// Gets or sets a PointF structure, which stores the X and Y coordinates of a 3D point.
  4122. /// </summary>
  4123. [
  4124. Bindable(true),
  4125. DefaultValue(0),
  4126. SRDescription("DescriptionAttributePoint3D_PointF")
  4127. ]
  4128. public PointF PointF
  4129. {
  4130. get
  4131. {
  4132. return this._coordXY;
  4133. }
  4134. set
  4135. {
  4136. this._coordXY = new PointF(value.X, value.Y);
  4137. }
  4138. }
  4139. #endregion
  4140. #region Constructors
  4141. /// <summary>
  4142. /// Public constructor.
  4143. /// </summary>
  4144. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly",
  4145. Justification = "X, Y and Z are cartesian coordinates and well understood")]
  4146. public Point3D(float x, float y, float z)
  4147. {
  4148. this._coordXY = new PointF(x, y);
  4149. this._coordZ = z;
  4150. }
  4151. /// <summary>
  4152. /// Public constructor.
  4153. /// </summary>
  4154. public Point3D( )
  4155. {
  4156. }
  4157. #endregion // Constructor
  4158. }
  4159. }