diff --git a/GameModel.md b/GameModel.md deleted file mode 100644 index 06d8ce1..0000000 --- a/GameModel.md +++ /dev/null @@ -1,238 +0,0 @@ -Game Model -=== - -The game is as follows - -There are 3 initial parties: Client (C), Worker (W), Verifier (V) - -The Client comes with a take-it-or-leave-it offer that Worker and Verifier can agree to sign. The offer is a contract to do calculations (Work) for a Payment, which is split in a predetermined way between W and V. The Worker shall calculate all the Work, while the Verifier shall calculate only a small portion of Work that is called Sample (described more fully at Stage 2). - -According to the contract, the Payment, the Worker Collateral Stake (w) and the Verifier Collateral Stake (v) are deposited to Escrow, to be released under certain conditions described in this text. Since calculations incure costs (hardware, electricity), the incentive to report false results without doing actual calculations is countered by providing collateral stakes that can be slashed if cheating is detected. It is economically logical that v is larger or equal to w, where the case w=v has certain merits of symmetry against attacks on stake. It is also economically logical that Verifier's work should be more expensive (per unit of calculations). This is because Verifier's stake is locked for approximately the same time as Worker, but Verifier's total reward is less than Worker's total reward (since much less work is done). - -The first taker of the contract will become Worker. However, the Verifier should be randomly chosen between a sufficiently large pool of volunteers who have made an Initial Deposit (not necessarily equal to v, can be smaller), so as to reduce the possibility of collusion between W, V or C, V. - -The full execution of the contract is as follows: - - -Stage 1: Selection of Worker ---- - -The Client and the Worker come to agreement about contract terms. The contract for Work is signed by (at least) C, W - this ends stage 1. - -Until the contract is signed, there are no further stages. - - -Stage 2: Worker's calculations of Work ---- - -The Worker proceeds to calculate the Work. Then, a part of ready Work is chosen as a Sample for verification purposes (according to a known tech process not described here). The Sample is chosen in a way that the Worker does not know which part of Work will be checked and therefore cannot choose which part of Work to calculate selectively. An economically feasible size of such Sample is between 5% and 20% of Work. The Sample result is reported to the Client - this ends stage 2. - -If the Worker fails to send the Sample result by a certain time, then he is penalized (not necessarily with a full stake w), and a replacement Worker can take this contract. Alternatively, if that it easier, the contract could be dissolved and everyone except the Worker receives full refund of deposits, while the Worker is penalized (not necessarily with a full stake w). This penalty prevents the stalling attack. The money from penalty could be burnt or given to the Client (and/or Verifier, if he was already chosen) as a compensation for wasting time. - -If the Worker sees that data are invalid and cannot be used for calculations, then there are various options to deal with the problem. One easy option is to allow the Worker to proceed as normal with the contract, and the Verifier and Arbiters should consider any results reported by the Worker as correct, and Client must give the full amount of Payment for this Work. This option has transparent design and should lead to a provable Nash equilibrium. Other options, in my opinion, are less solid and should be considered only if the first option is impractical for "marketing" reasons (it punishes Client's mistakes harshly). - -The second option is to introduce a stage 1.5 which allows the Worker to back off from the contract after seing the data (if this is allowed without penalties, it makes possible a stalling attack by the Worker, but if we add a small penalty, it makes possible a flooding attack by the Client, so neither solution is perfect). Another option is to have Worker and Verifier both agree on poor quality of data, in which case the Client can receive refund of Payment minus a small penalty (while if Verifier does not agree with Worker on poor quality of data, then either Arbitration, or the Worker can back off from the contract with some penalty). Whether the Client should be entitled to start Arbitration over the quality of data is questionable: it creates a tradeoff between burdening the Arbiters with the trivial task of data checking (if C, W, V do not all agree) and assigning minor penalties to Client that cannot be protested in Arbitration. - - -Stage 3: Selection of Verifier ---- -_(not strictly in this order, can be inserted earlier)_ - -Potential Verifiers make Initial Deposits (not necessarily equal to v), declaring that they agree with the contract terms. The actual Verifier is chosen randomy among them and signs the contract for Work - this ends stage 3. Note thah the gathering and /or selection of Verifier could have been done at any time earlier; the timing is not critical, but it affects the length of time that the Verifier's stake is locked. There is a minor practical trade-off: choosing a Verifier late rather than early allows to minimize the time that the Verifier's stake is locked, however it creates more uncertainty whether the Verifier will be found in time. - -If the chosen Verifier fails to sign the contract by a certain time, then he is penalized with the size of his Initial Deposit. This penalty prevents the stalling attack. The money from penalty could be burnt or given to the Client and/or Worker as a compensation for wasting time. - -An alternative approach to the process of Selection of Verifier is to make the choice of Verifier's be totally Client's responsibility. That would affect the incentives of participants in many ways. It would take away the safeguards that come from the fact that parties were not initially related to the Client and therefore not colluding with the Client by default, and that such a collusion was relatively rare. If such a collusion is trivial, then the main remaining safeguard would be the Arbitrage. It is not an impossible setting, but it lays much greater responsibility on the Arbitrage and gives more incentives for Arbiters corruption. As a side note, it may be hard for the honest Client to pick an honest Verifier, since the Client is not well versed in random procedures and is likely to choose whoever comes first, or from a small initial pool, and those could more easily be Worker's collaborators. - -Stage 4: Verifier's calculations of Sample ---- - -The Verifier receives the information how to calculate the Sample. The Verifier calculates the Sample and reports to the Client. Hence, the Client gets the information whether the Worker's results are considered correct by the Verifier - this ends stage 4. Note that it is not critical whether the Verifier received the Sample value calculated by the Worker. Both settings are possible: a) where the Verifier reports his own Sample result (without knowing the Worker's Sample result) and b) where the Verifier reports only a binary agreement or disagreement with the Worker's Sample result. However, the setting (a) is better, as it makes collusion between W and V more costly. - -If the Verifier fails to report to the Client by a certain time, then he is penalized (not necessarily with a full stake v, however it should be a significant penalty, not a minor one). This penalty prevents the stalling attack and other combined attacks. In this case, a replacement random Verifier needs to take over the contract, perhaps one that is chosen initially as a replacement. The money from the penalty could be burnt. If the Verifier's failure to report to the Client can be detected automatically then some of penalty the money could given to the Client and/or Worker as a compensation for wasting time. However, if the Verifier's failure to report to the Client is not automatic, but needs to be confirmed by the Client, then it wiser not to give to Client any monetary rewards for penalising the Verifier, and the Verifier should be able to appeal to Arbitrage. - -More generally, whenever and party receives money extracted from the stake of another party, this creates incentives to attack by misreporting another party's failure. This is why any significant portions of stake taken from W or V should never be fully split between C, W, V - a large part of the penalty money should be burnt (unless used to pay for Arbitration), while minor compensations can be given. The stakes exist mostly to provide payment for Arbitration, if such a need arises. Minor compensations to C, W, V may be useful to offset attacks that waste time of network participants (I call these "stalling attacks"). - -If inserting a replacement Verifier into a contract is technically very hard, there is another possibility. The contract could be dissolved. In that case, the Worker is paid for the Work fully, by money from Verifier's penalty. The Client receives back his full Payment plus the Work results, unverified. - -As a sidenote: a question may arise, if the Client, having received the Sample calculations by two parties (which in this case would be two Workers) can be entrusted with reporting whether the results agree. That would make the Verifier's role redundant. However, in my opinion, the one who produces the Sample calculations checking (whether we call him V or "another W") should also be the one who reports whether it agrees with the initial Worker's result. Or (even better) if this can be done automatically by comparing the two values. The Client, of course, can easily understand whether two values, reported to him, agree or not - however, preferrably he should not be the one who reports this. Adding agents (including the Client) to the chain of information flow would complicate incentives and create "broken phone" situations, where information changes as it passes through agents and it is hard to find who has modified it. - -Stage 5: Payment release and obtaining of results by Client ---- - -Having already received the Sample results and the Verifier's report, the Client chooses either to release the Payment voluntarily and receive the full Work results, or to ask to back off and not receive the full Work results - making the choice ends stage 5. - -Note that the ultimate responsibility of checking the Sample quality is on the Client. The Client may, for example choose to do the check himself, or outsource to specialists. However, the usefulness of a Verifier becomes obvious here. There are reasons why the Client many find it more practical to proceed without centralized checking when W, V agree. Some possible reasons are: a) costs of calculations b) there could be cases where having a small percentage of faulty calculation does not create problems, especially compared to variance in reliability of data itself, c) transaction costs (having to sign legal contracts, do paperwork, fill orders for multiple small transactions etc.) d) the subtleties of complying with international regulations can make people and businesses be unwanted as clients, based on their country of origin, due to censorship reasons etc. - -An additional reason for Verifier's existence is having network's own protection against cheating, rather than outsource it to Client's agents who could be corrupted, or regulated into censorship, or having an incentive to act with malice against the network. - - -Stage 6: Payment settling ---- - -1. If the Client has voluntarily released the payment: - - If W and V were in agreement, then W and V get the pre-agreed payments for their contribution to the Work. - - If W and V were not in agreement, then the following distribution happens, unless canceled by V within a certain time limit. By this distribution, W gets the pre-agreed payment for his contribution to the Work, and V loses a significant part of the stake (around 75% would be an economically logical area that averts cheating, and still leaves enough incentives to V not to go to Arbitration without good reason). This penalty money is mostly burnt; a small part of money might be awarded to the Client for his contribution to network safety and/or for costs to have to re-check the Sample results because of W, V disagreement. If V cancels the distribution within the given time limit, then Arbitration. - -Explanation: If the Client has voluntarily released the payment, this means that he desires to receive the results and is ready to pay for them. This also means that the Client agrees with the Worker, not with the Verifier. Hence the process starts by offering to Verifier to surrender most of its stake. - -2. If the Client selects that he will not voluntarily release the Payment, i.e. asks to back off (on the grounds that he is not satisfied with the Sample result quality: - -- If W, V were not in agreement, then the following distribution happens, unless canceled by W within a certain time limit. By this distribution, V gets the pre-agreed payment for his contribution to the Work, and W loses a significant part of the stake (around 75% would be an economically logical area that averts cheating, and still leaves enough incentives to W not to go to Arbitration without good reason). This penalty money is mostly burnt; a small part of money might be awarded to the Client for wasting time. If W cancels the distribution within the given time limit, then Arbitration. - -Explanation: If the Client does not release the payment voluntarily, this means that he is not desiring to get the results. This also means that the Client agrees with the Verifier (when Verifier claims that the Worker is faulty), not with the Worker. This is why the process starts with the offer to Worker to surrender most of its stake. - -- If W, V were in agreement (a rare case where W and V agree, but not C), then the following distribution happens: all three agents C, W, V receive a small penalty which is burnt. The remainder of Payment is refunded to the Client, while the remainder stakes are returned to W, V. The Client does not receive the results. Arbitration cannot be initiated, because there is a chance that W, V are found innocent and there will be no stakes cuts to cover Arbitration costs. - -There arises a question: if the Verifier was to be selected by the Client, on Client's full responsibility, could we then omit the Client's choice not to release the payment voluntarily when W, V results agree between themselves? This is possible. That way the Client effectively would lose the right to protest, and his interests are represented by Verifier only. However, it might still be beneficial to keep separate the right of agents to protest, so that the faulty Verifier that conspires with the Worker can be deterred. - -3. If the Client fails to reply about his choice within the given amount of time: - - If W and V were in agreement, then W and V get the pre-agreed payments for their contribution to the Work. - - If W and V were not in agreement, then Arbitration. It may be reasonable (if not too difficult techically) to give them the chance to avoid Arbitration, by agreeing collectively to either of the two distributions: (W paid, V penalized 75%), (W penalized 75%, V paid). - -Stage 7: Arbitration (optional) ---- - -Arbitration happens only if no agreement about distribution of payment and locked stakes was reached in the previous stage. To become eligible, Arbiters would need to put their own stake as collateral (comparable in size to stakes of W, V), so as to judge responsibly, having skin in the game. - -Arbitration is done with as many (random) Arbiters as possible. The limitation to the number of Arbiters is the collateral stake size that pays for Arbiters calculations; the collateral stake of the faulty W or V are distributed among Arbiters. The payment of Arbiters must not be different, whether they exonerate W or V. This is achieved by either having symmetric stakes w=v (right from start or when going to Arbitration), or by fixing Arbiters total payment as being the smallest from v, w. The Client's locked Payment never goes to the Arbiters, but is either refunded to the Client (when V is found true) or awarded to the Worker (when W is found true). - -One option is that Arbiters decide the outcome of Arbitration by simple majority, in that case they should be an odd number. Another alternative option is to design incentives for Arbiters, to discourage corruption, making it unprofitable. For example, if 70% of Arbiters agree on any outcome, then they are rewarded and those Arbiters that do not agree are seriously punished by stake cut. If less than 70% Arbiters agree on any outcome (i.e. the agreement rate is between 50% and 70%), then the totality of Arbiters is slightly punished and this money is used to re-do the Work anew or to refund the Client (while W, V get their stakes back untouched). - -Also, it is worth noting that when Arbitration starts, most previous choices of C, W, V become irrelevant, e.g. who initiated the Arbitration and on what grounds is irrelevant; the Arbitration is a greenfield approach. - - -Aligning of economic incentives -=== - -Let `p` be the probability of cheating detection, `s` the standard payment -for work and `c` the cost to produce the calculation. - -Let the standard collateral stake, lost when cheating is detected, be `w` -for Worker node and `v` for Verifier node, where `v \geq w`. - -In order for the network to operate, producing nearly perfect results in -equilibrium, cheating must be unprofitable. - -Assumptions ---- - -1. Client wants to pay only if payment is required in order to receive correct calculations -2. Client does not want to pay to receive faulty or missing calculations -3. Worker wants to spend resources to do correct calculations only if its payment is more than the costs incurred -4. Worker may not want to spend resources to do correct calculations, if there is a possibility to get paid for faulty or missing calculations. - - -Prevention of worker cheating ---- -*Collateral stake size vs probability of detection trade-off* - -Let's calculate the expected profits from cheating. The payoffs for each -world condition are given in the following Table. - -Decision to cheat | Payoff ------------------ |:------: -True node | `s-c` -False node (undetected) | `s` -False node (detected) | `-w` - -The expected payoff when not cheating is `s-c`, i.e. standard payment for -the assignment, minus the incurred cost. - -The expected payoff when cheating depends on the probability of detection `p`% -. Hence it is `(1-p)s+p(-w)`, i.e. the expectation of standard payment `s` -without any doing work minus the expectation to lose one's stake collateral -deposit `w`. - -Hence the expected payoff for the strategy 1 is as follows: - -Decision to cheat | Expected payoff ------------------ |:---------------: -True node | `s-c` -False node, dissolve if detected | `(1-p)s+p(-w)` - -Therefore, cheating is not profitable when `s-c>(1-p)s+p(-w) \Longrightarrow -p(s+w)>c` - -Observe that the result depends on all four parameters. It depends -positively on the probability of detection `p`, the standard payment `s` and -the stake `w`; it depends adversely on the cost of calculations `c`. - -In order to quantify the above results, we need to make reasonable -assumptions about the relation between payment `s` and cost `c`. This -depends on the "markup" (profit margin) `\frac{s-c}{c}`. If `\frac{s-c}{c}=0` -then this markup is 0\%, therefore, `s=c`. If `\frac{s-c}{c}=1` then this -markup is 100\%, therefore `s=2c`. - -The Table below uses the formula `p(s+w)>c` and shows how high the stake `w` -need to be, relative to calculation cost `c`, in order to deter cheating. - - `p` | `\frac{s-c}{c}` | `w` -:---:|:---------------:| ---: -`0.02` | `0` | `49c` -`0.02` | `1` | `48c` -`0.05` | `0` | `19c` -`0.05` | `1` | `18c` -`0.1` | `0` | `9c` -`0.1` | `1` | `8c` -`0.2` | `0` | `4c` -`0.2` | `1` | `3c` - -Therefore we find that e.g. a combination of Worker stake `w=9c` and -repetition of calculations (i.e. probability of detection) `p=10\%` would be -sufficient to make cheating unprofitable, regardless the markup level. - -Note that the results are not very sensitive to a change in the markup. -Instead, they are quite sensitive to a change in probability of detection `p`%. -This is because the biggest impact on incentives comes from losing the -collateral stake rather that from losing the payment for work. Hence a -simplified, practical rule of thump `w \geq \frac{c}{p}` could be used. The -tradeoff is between increasing the probability of detection `p` by costly -repetition of calculations, versus having too high a stake that workers -cannot afford. - -If `c` is unobservable on the market, we can logically assume that `c\leq s` -(otherwise the worker would operate at loss) and therefore we can use `s` as -proxy for maximum `c`. Therefore, the rule of thumb becomes `w \geq \frac{s}{p% -}`. - -Prevention of Verifier(s) cheating ---- - -The probability of detection `p` is independent of the number of Verifiers -that work on a task; it depends only on the percentage of work -re-calculated. Any additional Verifier that repeats calculations increases -the customer's cost sharply. However, increase in the number of Verifiers -could potentially help to prevent collusion between the Worker and the -Verifier (two Verifiers would be sufficient to create a Prisoner's Dilemma). -Collusion can happen if the Worker could communicate to the Verifier the -result that the Worker intends to send to the network. Knowing that result -(and assuming that the Verifier believes the Worker's declaration), the -Verifier could send the same result without doing the calculations. - -Since we cannot predict the futuristic technologies that can facilitate collusion -(like smart contracts and automated prediction markets), it is easier to -approach the problem by calculating the budget that the Worker node has to -spend for facilitation of collusion. The Worker's maximum gain from getting -paid without procuring any cost is `s`. However, the gain of -Verifier from reporting the Worker's results as faulty is `w` (the -confiscated worker stake), where `w > s`. In addition, as long as `v > w`, it is -easy to setup bounty programs where Worker could benefit from luring the -Verifier to collusion (by sacrificing `w)` and then reporting him to the -customer's arbitration to gain `v`. The Prisoner's dilemma already exists -between the Worker and the Verifier, without the need for a second Verifier. -Hence a rational Verifier will not collude with the Worker, unless of course -they both happen to belong to the same actor. But any actor large enough to -benefit consistently from such a coincidence is a large enough player to -have its incentives aligned with the network's success and would likely not -undermine it by cheating. - -Possible corruption scenarios ---- - -[ ] TBD @sabina-sa - -References -=== - -[ ] TBD @dr-orlovsky @sabina-sa diff --git a/Issues.md b/Issues.md deleted file mode 100644 index 809d6e3..0000000 --- a/Issues.md +++ /dev/null @@ -1,20 +0,0 @@ -Paralleleism for trainings ---- - -Federated learning - -Non-determinism for GPU computing ---- - -Number rounding before hashing - -Data & model privacy ---- - -Homomorphic encryption -protects data used for training/inference and model used for inference - -Federated learning - -Private agreements - diff --git a/Protocol.md b/Protocol.md deleted file mode 100644 index cb9ac1e..0000000 --- a/Protocol.md +++ /dev/null @@ -1,35 +0,0 @@ -Protocol -=== - -Setup ---- - -The protocol design includes the following types of participants: -- Client node `C`: party prepared to pay for the actual computing and provide necessary data for it. -- Worker node `W`: party prepared to perform actual computing for some reward `s`. -- Verifier node `V`: party prepared to verify the actual computing using PCP for some reward `s / z << s` - -The client node splits the actual computing task into *batches* suitable for parallel processing in a distributed P2P network. - -For each batch, the client selects Client and Verifier nodes on some open market or by auction (the actual selection is not part of this protocol). They agree upon the price of the computing and stakes (see below) in the process of off-chain P2P communications. - -To secure the actual computing Worker and Verifier node prepare payments with stakes `w` and `v` correspondingly. All three parties secure costs `s+w+v` under special contract on the Bitcoin blockchain (see section "Smart Contract" below for the contract details) and wait for a necessary number of confirmations (up to their agreement). - -Contract execution ---- - -The Worker node receives all necessary data from the client (via p2p channel or by other means, like IPFS) and performs the actual batch of computing with the data after splitting them into a separate **computing steps**. - -For different computing models a step can be defined in such ways: -* *Map-reduce*: a snapshot of input data with the corresponding result for each `map` and `reduce` functions call -* *Inference on pre-trained machine learning model*: each input for the model with the corresponding result produced by the model -* *Training of machine learning model*: weights matrix for each step of the training - -Worker node performs actual computations and runs the following non-interactive PCP scheme (see Fig. 1): for each computing step (see definition below) the Worker has to compute the hash value (**commitment**). Commitments are merkelized into a tree. The hash of the Merkle tree root is used as a random oracle to sample some pre-agreed amount of the commitments. For each sampled commitment a proof is constructed, consisting of an index of the source computing step input, its resulting value and the Merkle path to the Merkle tree root. - -[ ] Add PCP scheme figure @dr-orlovsky - -These data are sent by Worker node to Verifier together with unlocking transaction (see "Smart contract" below) signed by `W`. Verifier checks the proofs by (a) checking the random oracle and (b) repeating sampled computations against their proofs. If the results are correct, Verifiers signs the unlocking transaction with its private key (`V`) and passes it back to the Client node. If the results are incorrect, the Verifier notifies the Client node and does not sign the unlocking transaction. - -The Worker has the right not to sign the received unlocking transaction if he is not satisfied with the results of computation by any reason (the protocol still be secure if it works under assumptions provided in "Economic incentives" section below). If the Worker decides to sign the transaction, he sends it over to the Bitcoin network to settle the payments. - diff --git a/README.md b/README.md index 30e9e8c..6fad573 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,7 @@ Prometheus === -**_Censorship-resistant trustless protocol for high-load computing & machine learning on top of Bitcoin smart contracts_** - - -*Working Draft* +**_Scalable High-load Computing & Machine Learning on top of Bitcoin_** Maxim Orlovsky1, 2, Sabina Sachtachtinskagia1, 3 1Pandora Core AG, Switzerland; @@ -13,25 +10,6 @@ Maxim Orlovsky1, 2, Sabina Sachtachtinskagia1, 3 ![Prometheus Logo](assets/prometheus.svg) -Introduction ---- - -The progress in the field of computing led to the appearance of cloud computing platforms. The success of cloud computing was related to the economy of scale phenomenon, enhanced by the exponentially increasing amounts of produced data. However, it has created systematic risks and fragility, namely privacy risks, possibilities of totalitarian control, surveillance, single points of failure and possibilities of censorship. It has put at risk the further development of machine learning technology, started to be monopolised together with the access to big data. [The #FreeAI Manifesto](https://manifesto.ai), had declared an initiative to oppose this dramatic trend, and this work aims at the development of the technology stack, which can address the issue. - -First, let's split the problem of censorship-resistant computing into parts: -1. Finding and buying information (big data, machine learning models) in a censorship-resistant way. -2. Owning and keeping information (big data, machine learning models) private (with possible plausible deniability). -3. Performing high-load computing on big data in anonymous and censorship-resistant and (desirable) private manner (for the cases when personal computing resources are not enough to perform the actual computation). - -Here we address only the third matter. The first two matters can be addresses with many other existing initiatives, such as dark anonymous markets with escrow, cryptography technologies, zero-knowledge, distributed storage and data transfer (Torrent, IPFS etc.). - -The technical problem we need to address here is how to prove computational integrity (correctness of the actual computations) made by some non-trusted (anonymous) the third party without repeating the whole volume of the actual computing. This issue is usually addressed by use of Probabilistically Checkable Proofs (PCP) [], however, they leave untouched the question of coupling the payments for the consumed computational resources with the actual proof. Here we aim to utilize existing Bitcoin smart-contracts (Bitcoin script) functionality to be coupled with PCP proofs at the second layer on top of actual Bitcoin blockchain with just two on-chain transactions, reducing the footprint, price and increasing scalability for the real-world business cases for the computing. Furthermore, the solution can work on top of the recently proposed [Typhon sidechains](https://github.com/dr-orlovsky/typhon-spec) giving even better scalability without putting a single transaction on-chain for multi-contract computing cases. - -Here we analyse parallelizable categories of high-load computing, including (but not limiting to) map-reduce tasks and inference on pre-trained machine learning models (training of machine learning models can be converted down to parallelizable case with existing federated learning solutions []). +Censorship resistant decentralised computing is a new topic in distributed systems theory. With the recent rise in popularity of Bitcoin as a censorship-resistant financial and transactional system a new technological field of blockchain and its applications appeared. This work addresses the problem of computational integrity proofs (correctness of the actual computations) made by some non-trusted (anonymous) third party without repeating the whole volume of the actual computing. The issue is usually addressed by use of probabilistically checkable proofs (PCP), however, such simple approach leave untouched the question of coupling the payments for the consumed computational resources with the actual proof. The purpose of our protocol is to scale distributed calculations by obtaining results of nearly perfect (measurable) quality, without the need to repeat calculations or check the computational integrity proofs by the client. This is achieved by providing meritocratic economic incentives to all participating economic agents, so that their interests are aligned with the network's success. As a result, the need for repeat calculations diminishes, and costs of calculations become lower and competitive. Ability to run PCP schemes without trusted checking party provides a way to codify the actual protocol in terms of smart contracts, so it can be used in a very compact form with existing economic blockchain layer. Our approach to protocol design aims at the smallest possible size of the shared system state so it can be implemented with any non-Turing complete smart contract system with storing nothing else then the account balances for the involved parties. For example, the protocol can be implemented with the existing Bitcoin script functionality with only two on-chain transactions per whole protocol computing cycle. In such system the PCP proofs are run the second layer on top of actual Bitcoin blockchain, reducing the footprint, price and increasing scalability for the real-world business cases for the computing. -This document consists of the following parts: -1. [Description of game model in terms of game theory](./GameModel.md) – *nearly complete* -2. [Overall protocol design for implementation of the developed game model](./Protocol.md) – *outdated, will be updated soon basing on a new game model developments* -3. [Implementation of protocol using Bitcoin-script based smart contracts and state channels](./SmartContracts.md) – *outdated, will be updated soon basing on a new game model developments* -4. [Wire protocol for peer-to-peer communications for running state channels](./WireProtocol.md) – *not ready yet* -5. [Solutions to a specific problems in implementing particular machine learning algorithms](./Issues.md) – *not ready yet* \ No newline at end of file +[Check the full PDF version of the paper](./prometheus.pdf) diff --git a/SmartContracts.md b/SmartContracts.md deleted file mode 100644 index 582e824..0000000 --- a/SmartContracts.md +++ /dev/null @@ -1,75 +0,0 @@ -Smart contract -=== - -Payment settlement schemes ---- - -There are 8 possible combinations of the Worker, Verifier and Client node signatures on the unlocking transaction:(`2^3=8`), out of which 3 are nonsense/invalid (since they include either Client of Verifier signature, which does not make economic sense: there is no reason to pay if the actual computations were not made). Thus, the unlocking script of the Prometheus-type smart contract defines 5 scenarios with corresponding rewards: - -Scenario | Signatures | W txout | V txout | C txout | Script branch ----------------------- |:----------:|:-------:|:-------:|:-------:| ----------- -Worker unresponsive | none | | | | CLTV return of funds to `C` -Everything correct | W+V+C | s+w | s/z+v | 0 | 3/3 multisig -Client declined to pay | W+V | w+x% | v | s-x% | 2/2 multisig -Faulty Verifier | W+C | s+w | 0 | 0 | 2/2 multisig -Faulty Worker | W | 0 | v+w+s/z | 0 | 1/1 multisig - -Contract script ---- - -Let: -- `W` be a signature of a worker with stake `w`, -- `V` be a signature of a verifier with stake `v`, -- `C` be a signature of client, -- `s` the agreed payment from the client for a successful contract execution, -- `t` be an agreed maximum time for the contract execution. - -All the stakes (`w+v`) and client payment (`s`) are deposed as tree *txins* with a single *txout*, locked with P2SH script. - -Transaction script for hashing into `ScriptPubKey`: -``` --- Branch for 3/3 multisig: everything is correct -OP_DEPTH -<3> -OP_VERIFY -OP_IF - OP_DROP2 - 3 3 - OP_CHECKMULTISIG - --- Ok, something went wrong: -OP_ELSE - --- Branches for 2/2 multisigs - OP_DROP - <2> - OP_VERIFY - OP_IF - OP_DROP2 - --- "Client declined to pay" case (W+V multisig) - 2 2 - OP_CHECKMULTISIG - --- "Faulty Verifier" case (W+C multisig) - OP_NOTIF - 2 2 - OP_CHECKMULTISIG - OP_END - --- Branch for "Faulty Worker" case: 1-of-1 Worker multisig - OP_ELSE - OP_DROP2 - 1 1 - OP_CHECKMULTISIG - --- Branch for "Worker unresponsive" case: CLTV return of funds to `C` - OP_NOTIF - 1 1 - OP_CHECKMULTISIG - - OP_CHECKLOCKTIMEVERIFY - OP_END - OP_END -OP_END -``` diff --git a/WireProtocol.md b/WireProtocol.md deleted file mode 100644 index 15ffe6e..0000000 --- a/WireProtocol.md +++ /dev/null @@ -1,12 +0,0 @@ -Commands ---- - -Inbound - -`preparecontract` - -`contractcommit` - -`` - -Outbound diff --git a/assets/prometheus.png b/assets/logo.png similarity index 100% rename from assets/prometheus.png rename to assets/logo.png diff --git a/assets/prometheus.svg b/assets/logo.svg similarity index 100% rename from assets/prometheus.svg rename to assets/logo.svg diff --git a/assets/pandora-network.svg b/assets/pandora-network.svg deleted file mode 100644 index 6e89df2..0000000 --- a/assets/pandora-network.svg +++ /dev/null @@ -1,2 +0,0 @@ - -
Pandora Peer Node (Rust)
Pandora Peer Node (Rust)
Computing node (Python)
Computing node (Python)
Computing node (Python)
Computing node (Python)
Computing node (Python)
Computing node (Python)
IPFS
IPFS
Pandora Broker Server (Elixir)
Pandora Broker Server (Elixir)
Pandora Peer Node
(Rust)
Pandora Peer Node<br>(Rust)<br>
Computing node (Python)
Computing node (Python)
IPFS
IPFS
IPFS
IPFS
Light Clients
(NodeJS, Go)
Light Clients<br>(NodeJS, Go)
Bitcoin
Bitcoin
Bitcoin
Bitcoin
Bitcoin
Bitcoin
Wire Protocol (TCP)
Wire Protocol (TCP)
Protobuf
Protobuf
Websockets
Websockets
Censored Networks
Censored Networks
Wire Protocol (TCP)
Wire Protocol (TCP)
\ No newline at end of file diff --git a/assets/pandora-network.xml b/assets/pandora-network.xml deleted file mode 100644 index fbf286e..0000000 --- a/assets/pandora-network.xml +++ /dev/null @@ -1 +0,0 @@ -7Vpbd5s4EP41Pif70B7usR8bx+le0tRnvXvSPsqggGoZcYSI7f31O4AwBmHHbbk0lydLgwTi+0Yz3wiPzOl6+5GjKPjEPExHhuZtR+b1yDD0saPDT2rZSYs+sXOLz4knbaVhQf7D0qhJa0I8HFcGCsaoIFHV6LIwxK6o2BDnbFMd9sBo9akR8rFiWLiIqtZ74okgt9qaVtp/x8QP5JNtW15Yo2KsNMQB8tjmwGTORuaUMyby1no7xTQFr4Aln3dz5Op+XRyH4pwJ679m27tvGnt3Mw0cev3pzoz+fTfO7/KIaCLfVy5W7AoAsAd4yC7jImA+CxGdldYrzpLQw+ljNOgFYk2hqUPzGxZiJylFiWBgKu9wy1gkx+HQ+5BSBd0lZe4qN90QWtwoFoiL+pDMeDAoX3m63KMISVPMEu7iE7AUjoa4j8WJccaeR9gAmK2x4DuYxzFFgjxW14GkI/r7cSVZ0JB8fQd3kzapwlsivhy0v6ZD3hu27F5v5ZSssys6IbzHl8NOPs0uuuW0rFfMO4vOJ53iXO8awC+sIf2iiJxdOsblm2P8kGOYgzqGEuznKPQYR2CcY8zh5w4SOPxc/J3E4jfFjapOsgmIwIsIZcBsIP1XHUc+DHOBt6fxVfHYFslUaoVCPFjStTdlJtaL/BocZGFH6whCQ4FwytZRIkjogzmU6M134G3h8Phdmr8afuazxs8wh8bPetb42UPDZyvw/TG/WSg4gVSP0qa7owQA40+DtcyRvV3uDchd+RnenxMBd8EtRsVxbVdrKqxOA6p2Z0FRVxB8U6JdC45zFccRX+pHcbRbTr5J0XY9wx7SNfRTahR8YJXp0QXmj1njYkbJlvDhc5qp1zRBoVEHS2q6Kkrb3HXP9xDHPncjOIPGSFXTtUlfv3GsjLZlgP16eO1ItB2E9p9kXU6dMwJL3McHpxYfTK227/N1yVk139kv4yeigapxj1T5DhWpUAWD46etouiv2wcPufVjAHM8dB2RR4xnW4f9AgBOFABfQCVmNZwP9FuJaS8BVtOquas+NKzdfiYbKkV/X0HUYoo2nHNTdDc52tRqGr7Yxz3laENNH7eZGxvalBKAM27IzmnS/hO2MjxS+8iGTyp6HcTBCyFTLYSuiHCBxeMBMFniFoOfh/jqM9yGiGzXvNfsqtHIrNmezzfqpKWIadS4aIiY+2PCXkKmqcrQV8KFXWB/6njW7pMKNdq8Fiq0KhWN+qxXKlQhcU94WivMORPMZekdLv6ZztXwDiiIumxIT8mmjDLQb9chS+G+egBBUDMhSvwwJRVQy6ReiilxEf0gL6yJ59FjfFfTSgukWPWPaueqO7srUtRaJGNjmTy8XBacy6dLwiYWjK5YsNTkfY+XMShdnMqhl8qDYVVDVJOK6peHhk/0OIwZh7c2tDssNoyvVELgziSKU9j2OYSyxOtFiVq2U8Ww6TP9ZKyCqOtdBXpLPdR9dYFeOQnVz3PtHwj00C3/SJtXaeXfkc3Z/w== \ No newline at end of file diff --git a/assets/pluto-network.xml b/assets/pluto-network.xml deleted file mode 100644 index b4c9502..0000000 --- a/assets/pluto-network.xml +++ /dev/null @@ -1 +0,0 @@ -3ZdRc5swDMc/DY+7AzxI8tosTXZLb9lltz27oAKtQZwRIfTTz25MCHOTpXfZ2vUp9t+yLH5SkHHYNN/OJS/TG4xBOL4bbx32yfF9bxx66kcrrVG8SbBTEpnFRuuFdfYIRnSNWmcxVANDQhSUlUMxwqKAiAYalxKbodkdiuGpJU/AEtYRF7b6M4sp3amB6/b6ArIkNScHgVnIeWdrhCrlMTYHEps5bCoRaTfKt1MQGl6HZbfv+sjqPi4JBZ2zwV99+/7Fm9/88EJ8nM8Wyd1i+cF42XBRm+edYlHVOUgTNLUdCIl1EYN25jrsqkkzgnXJI73aqNwrLaVcqJmnhsYtSILt0Xi9PQVVPoA5kGyVidnQcesKh5l502fB62zSgwyERuMm8cnecw9HDQyfF7AaW6wsRhCr2jFTlJRiggUXs169GlI8IHYPRK0pf14TKqn3sEQsB2T1Qae5qriwlhGceCATP3GZAJ2wGz2fJwmCU7YZxnFx6r5FfSVxo94Jr1+hLHxrJcrsvzOPUrBIqXdRqYdRK7JCo/wjrtsd2+XtXuDRQ/JE/GtNygtcjmvwG9axjTV8hmrwt6h+tKh+Xl2v/2+ok1dmGrwHpmzytqBO3luHGp3ZoY6k6d90qJFdygS5vgJCVenb5bGijjFSNy3N5MVFfYHi9d0zupd7me6lpv1F92nt4HOBzX4B \ No newline at end of file diff --git a/assets/settlementflow.pdf b/assets/settlementflow.pdf new file mode 100644 index 0000000..912feee Binary files /dev/null and b/assets/settlementflow.pdf differ diff --git a/assets/strategies.pdf b/assets/strategies.pdf new file mode 100644 index 0000000..23dc1ac Binary files /dev/null and b/assets/strategies.pdf differ diff --git a/assets/txo.pdf b/assets/txo.pdf new file mode 100644 index 0000000..e114255 Binary files /dev/null and b/assets/txo.pdf differ diff --git a/assets/txtree.pdf b/assets/txtree.pdf new file mode 100644 index 0000000..b5f6931 Binary files /dev/null and b/assets/txtree.pdf differ diff --git a/assets/upsilon.pdf b/assets/upsilon.pdf new file mode 100644 index 0000000..c95bfa7 Binary files /dev/null and b/assets/upsilon.pdf differ diff --git a/prometheus.pdf b/prometheus.pdf new file mode 100644 index 0000000..f27dc69 Binary files /dev/null and b/prometheus.pdf differ diff --git a/prometheus.tex b/prometheus.tex new file mode 100644 index 0000000..bf614d5 --- /dev/null +++ b/prometheus.tex @@ -0,0 +1,544 @@ +\documentclass[a4paper]{article} + +\usepackage[utf8]{inputenc} +\usepackage[english]{babel} +\usepackage{authblk} +\usepackage{adjustbox} +\usepackage{enumitem} +\usepackage{caption} +\usepackage{xfrac} +\usepackage[pdf]{graphviz} +\usepackage{pgf-umlsd} +\usepackage{array} +\usepackage{makecell} +\usepackage{booktabs} +\usepackage{xcolor} +\usepackage{amsmath} +\usepackage{amssymb} +\usepackage{mathtools} +\usepackage{listings} +\usepackage{verbatim} +\usepackage{indentfirst} +\usepackage[T1]{fontenc} +\usepackage[colorlinks=true, allcolors=blue]{hyperref} +%\usepackage[a4paper,top=3cm,bottom=2cm,left=3cm,right=3cm,marginparwidth=1.75cm]{geometry} + +\babelhyphenation[english]{every-where} +\usetikzlibrary{shapes,arrows} + +\newcolumntype{M}[1]{>{\raggedright\arraybackslash}m{#1}} + +\lstdefinestyle{mystyle}{ + backgroundcolor=\color{lightgray}, + commentstyle=\color{codegreen}, + keywordstyle=\color{magenta}, + numberstyle=\tiny\color{lightgray}, + stringstyle=\color{codepurple}, + basicstyle=\footnotesize, + breakatwhitespace=false, + breaklines=true, + captionpos=b, + keepspaces=true, + numbers=left, + numbersep=5pt, + showspaces=false, + showstringspaces=false, + showtabs=false, + tabsize=2 +} +\lstset{style=mystyle} + +\providecommand{\tightlist}{% + \setlength{\itemsep}{0pt}% + \setlength{\parskip}{0pt}% + \setlength{\topsep}{0pt}} + +\title{\textsc{Prometheus}:\\Scalable High-load Computing \& Machine Learning on top of Bitcoin} +\author[1,2]{Maxim Orlovsky\thanks{orlovsky@pandoracore.com}} +\author[1,3]{Sabina Sachtachtinskagia\thanks{sabina.aueb@gmail.com}} +\affil[1]{Pandora Core AG, Switzerland} +\affil[2]{BICA Labs, Switzerland} +\affil[3]{Athens University of Economics and Business, Greece} + +\date{Version 0.1.0, working draft} + +\begin{document} + +\maketitle + +\begin{abstract} +This work defines Prometheus, a protocol for running high-load \& machine learning computing inside a trustless decentralized environment in Byzantine-tolerant way, utilizing bitcoin blockchain and script functionality, layer 2 state channels, zero knowledge computation integrity arguments, economic incentives and p2p networking. + +This work covers questions of Prometheus design rationale, architecture, formal protocol definition, transaction-based model and specifies protocol operations with demonstrable Nash equilibrium; the latter part is based on our previous work on incentive-based interactive settlement scheme for computation integrity proofs \cite{ibiss}. + +This work will be followed by the papers on (1) specific probabilistically checkable proof schemes for high-load \& machine learning computing, addressing the problems of non-determinism in artificial neural networks, data privacy and federated learning and (2) underlying P2P communication protocol for global state replication and state channel management. +\end{abstract} + + +\section{Background \& Rationale} + +The modern success of cloud computing is related to the economy of scale phenomenon, enhanced by the exponentially increasing amounts of produced data. However, it has created systematic risks and fragility, namely vanishing privacy, possibilities of totalitarian control, surveillance, single points of failure and censorship. It has put at risk the further development of machine learning technologies, which have started to be monopolised together with the access to big data. \href{https://manifesto.ai}{The \#FreeAI Manifesto} \cite{freeai} had declared an initiative to oppose this dramatic trend, and this work aims at the development of the technology stack able to address the issue. + +The problem of censorship-resistant computing consists of many parts, starting from finding and buying information (big data, machine learning models) in a censorship-resistant way, owning it with proper privacy and plausible deniability -- and up to computations using big data in anonymous and censorship-resistant and (desirably) private manner. Prometheus protocol addresses the latter issue, since the former two matters (finding and owning information) can be solved with many other existing initiatives, such as dark/anonymous markets, cryptography technologies, zero-knowledge, distributed storage and data transfer (Torrent, IPFS) etc \cite{ipfs, bittorrent, afs, tor, gnunet}. + +The actual technical problem we need to tackle in order to reach our goal is the proofs for computational integrity (correctness of the actual computations) for the computing performed by some non-trusted (anonymous) third parties -- without the need to repeat the whole volume of the actual computing. This issue is usually addressed by the use of probabilistically checkable proofs (PCP) -- however, they leave untouched the question of coupling the payments for the consumed computational resources with the actual proof. This question was resolved in our previous work \cite{ibiss}. + +Ability to run PCP schemes without trusted checking party provides a way to codify the actual protocol in terms of smart contracts, so it can be used in a very compact form with existing economic blockchain layer. Our approach to protocol design aims at the smallest possible size of the shared system state so it can be implemented with any non-Turing complete smart contract system with storing nothing else than the account balances (or UTXOs) and their un-locking conditions for the involved parties. For example, the protocol can be implemented with the existing Bitcoin script functionality with only two on-chain transactions per whole protocol computing cycle. In such system the PCP proofs are used at the second layer on top of slow blockchain, reducing the footprint, price and increasing scalability for the real-world business. + +\section{Generic Protocol} + +\subsection{Architecture} +\label{architecture} +A core of the protocol is based on the following main algorithms and cryptographic schemes: +\begin{description} + \item[IBISS-CIP] Incentive-based interactive settlement scheme for computation integrity arguments: a game theory model for providing provably-safe economic incentives \cite{ibiss} + \item[Bitcoin] including Bitcoin as a mean of payment, bitcoin blockchain as a safety and immutability layer, Bitcoin Script and HTLC smart contracts for the settlement algorithm logic. + \item[PCP] probabilistically checkable proofs \cite{pcp, pcpibm} as non-interactive computation integrity verification technology + \item[P2P Networking] for building decentralised network of nodes +\end{description} + +The protocol is designed in a modular way, with four main components: +\begin{description} + \item[Settlement algorithm] based on a game theory model with provable Nash equilibrium described in Section \ref{settlement} – an implementation of IBISS-CIP scheme \cite{ibiss} + \item[Transaction model] defining set of interdependent off-chain and on-chain transactions with corresponding HLTV/CLTV/multisig smart contracts, described in Section \ref{transaction_model} + \item[Networking protocol] for P2P node communications and information exchange + \item[HLML-PCP schemes] PCP extension schemes for high-load computing and machine learning-specific tasks (HLML) +\end{description} + +\begin{figure}[h!] + \centering + \begin{adjustbox}{center}\digraph[scale=0.75]{structs}{ + node[shape=record] + prometheus [ + label="{ + {Prometheus protocol} + { Settlement algorithm & Transaction model & HLML-PCP schemes & Networking protocol} + }" + ]; + bitcoin [label="Distributed ledger"]; + pcp [label="PCP"]; + ibiss [label="IBISS-CIP"]; + p2p [label="P2P networking"]; + + prometheus:sc -> bitcoin; + prometheus:zk -> pcp; + prometheus:sa -> ibiss; + prometheus:wp -> p2p; + }\end{adjustbox} + \caption{Architecture of the Prometheus protocol} + \label{fig:architecture} +\end{figure} + +Particularities of specific PCP gadgets for high-load \& machine learning computing, as well as P2P protocol are not covered by these document. + +\subsection{Scopes and Actors} +\label{scopes_actors} + +The protocol operates within two different scopes: \textbf{global scope} $\Gamma$ and \textbf{channel scope} $\Lambda$. + +The protocol design includes the following types of participants (actors): +\begin{description} + \item[Client node] $C$: party prepared to pay for the actual computing and provide necessary data for it. + \item[Worker node] $W$: party prepared to perform actual computing for some reward. + \item[Verifier node] $V$: party prepared to verify the actual computing using HLML-PCP. + \item[Arbiter nodes] $\mathbb{A}$ (global) and their subset $\mathcal{A}$ (for a given computing contract): a special sets of nodes committed to do the arbitration work + \item[Common fund] $F$: used as a wild card destination for penalty payments. This can be a deterministic unspendable contract, or a special contact for funding protocol development etc. The particular value form of common fund is defined by each of Prometheus contracts (see below). +\end{description} + +Clients, Workers and Verifiers (as well as the common funds) operate only within \textit{channel scope}, while Arbiters are defined within the \textit{global scope} (denoted by $\mathbb{A} \mid \Gamma$) and are able to participate in \textit{channel scope} (denoted by $\mathcal{A} \mid \Lambda$). + +\subsection{Layers, Contracts and States} +\label{layers_contracts} + +The protocol operates using different layers of abstraction, each of which contains its own set of contracts: +\begin{enumerate} + \item \textbf{Global consensus state} $\mathsf{G} \mid \Gamma$ based on blockchain-stored state $\Gamma^\Omega$ and off-chain data $\Gamma^\Theta$, committed to the blockchain with hashes and replicated by all parties participating the protocol: + \begin{description} + \item[Prometheus contracts] $\{ \mathsf{P}_i \} \mid \Gamma$: specially-formed contracts encoding pre-defined sets of global parameters. These are \textit{commitment-based agreement}, i.e. agreements that can be referenced by any independent party as a commitment to follow/accept these pre-defined values. Basically, it is a tuple of meta-parameters: + + $\mathsf{P} = \langle w, v, n, a, F, \alpha, \beta, \gamma, \delta, \varepsilon, \mu, \sigma, t_1, t_2, t_3, t_4 \rangle$ + + For the meaning of the constants see Table~\ref{table:state}. + + \item[Arbitration offers] $\{ \mathsf{O}_i \} \mid \Gamma$: specially-formed contracts from parties that want to act as Arbiters in any future computation contract. These contracts are committed to particular Prometheus contract $\mathsf{P}_i$ and must contain proofs of the locked arbiter's stake of size $a$ (defined by $\mathsf{P}_i$): $\mathsf{O} = \langle \mathsf{P}_i, a_p \rangle$. Parties that have made these commitments constitutes global set $\mathbb{A}$. + + \item[Computational contracts] $\{ \mathsf{C}_i \} \mid (\Gamma \vee \Lambda)$: \textit{economically-binding agreements} committed to some particular contract $\mathsf{P}_i$ between parties $C, W, V$ containing proofs of their locked stakes $w_p,v_p$ (corresponding to the amounts defined by $\mathsf{P}_i$), computational reward $c$ and computation-related $\mathtt{payload}$: $\mathsf{C} = \langle \mathsf{P}_i, C, W, V, c, w_p, v_p, \mathtt{payload} \rangle$. Computational contract locks deposits and includes all possible settlement schemes (see Table 2). + + \item[Arbitration contracts] $\{ \mathsf{A}_i \} \mid (\Gamma \vee \Lambda)$: \textit{economically-binding agreements} committed to some particular contract $\mathsf{C}_i$ by Arbiters (denoted as $\mathcal{A}$) that were randomly selected from $\mathbb{A}$ to participate in Arbitration stage of the computing contract. The contract contains proofs of the arbiter's stake commitments ($a_p$): $\mathsf{A} = \langle \mathsf{C}_i, \mathcal{A}, \{ a_{p_i} \mid \forall A_i \in \mathcal{A} \} \rangle$ + \end{description} + + \item \textbf{Multiparty state channel} $\mathsf{M}(\mathsf{C}_i) \mid \Lambda$ originating from each computational contract $\mathsf{C_i}$: $\mathsf{M}(\mathsf{C}_i) = \langle \mathsf{C_i}, \Upsilon, \pi, \mathtt{Encrypt}(\Pi, h_p)\rangle$. The channel operates the following state data: + \begin{description} + \item[Partially-signed transaction tree] $\Upsilon$, which defines: + \begin{itemize} + \item Current \textbf{operational state} of the channel (see below for the details); + \item \textbf{Hash-lock value} $h$ provided by the Worker and used in hash-locked transactions within $\Upsilon$ to bind the use of reward $c$ by the Worker with the ability of the Client to read the results of computation, encrypted with this hash \textit{preimage} $h_p$ (see below); + \item \textbf{Verifier's verdict} $r=\{\emptyset,0,1\}$ on Worker's proofs of computational integrity. Verdict equals to $\emptyset$ before the completion of the Verification phase (see below); + \item \textbf{Set of Arbiters} $\mathcal{A}$ (equals to $\emptyset$ befor the start of arbitration phase); + \item \textbf{State of arbitration contract} $\mathsf{A}_i$ and corresponding signed but unpublished transactions + \item \textbf{Arbitration decision} $d=\{\emptyset,0,1\}$: \textit{economically-binding decision} provided by $\mathcal{A^+}$ (subset of $\mathcal{A}$ which have signed to the decision). Equals to $\emptyset$ before the completion of arbitration phase -- or in case if no definite decision was reached. + \end{itemize} + \item[Non-interactive probabilistic proofs] $\pi$ of computational integrity provided by the Worker at the end of computation phase. + \item[Computation results] $\Pi$ encrypted with \textit{preimage} $h_p$ by the Worker. + \end{description} + + \item \textbf{Internal state} of the actors ($C, W, V, A_i$): each party acts as a part of channel state machine (i.e. it is replicated \& semi-synchroneous state machine). Thus, the internal state of the party is tightly related to the state of the channel (see below). When required, the current state of the party is denoted with precondition notation: for instance, $W\mid \Lambda^\mathsf{C}$ represents Worker node under computing state, while $\Lambda^\mathsf{C} \mid\: W$ represents channel in computing state with Worker node signed confirmation (agreement) on the state published into the channel. +\end{enumerate} + +For the complete list of state parameters see Table~\ref{table:state}. + +\begin{table}[hp] + \caption{Summary of state parameters} + \label{table:state} + \begin{adjustbox}{center} + \begin{tabular}{@{} l m{11cm} m{2.5cm} c @{}} + \toprule + Parameter & Description & Bounding\-conditions & Defined by \\ + \midrule +&\textbf{1. Actors, signatures and stakes} \\ + $W, V, C$ & Worker, Verifier and Client as actors identified by their signatures & $W \ne V \ne C$ & $\mathsf{C}$ \\ + $w, v$ & Stakes deposited by Worker and Verifier & \makecell{$w \gg z$, $v \gg y$,\\ $w \le v$} & $\mathsf{P}$ \\ + $c$ & Amount the Client is willing to pay for the computation & $c \gg z \gg y$ & $\mathsf{C}$ \\ + $z$ & Cost incurred by the Worker for performing the computation & $z \ll c$ & $W$ \\ + $y$ & Cost incurred by the Verifier for performing the verification & $y \ll z \ll c$ & $V$ \\ + $F$ & \textit{Common fund} public key/address which is controlled by external body (can be charity, donations to development, deterministic proof of burn address or anything else) & & $\mathsf{P}$ \\ + $\mathbb{A}$ & Global set of pre-registered Arbiters & $\mathbb{A} \ne \emptyset$ & $\mathsf{O}$ \\ + $\mathcal{A} = \{A_i\}$ & Set of randomly-selected Arbiters as actors identified by their signatures & $\mathcal{A} \subset \mathbb{A}$ $\mid\mathcal{A}\mid\: \ge 3$ & $\mathsf{M}$ \\ + $\mathcal{A^+}$ & Subset of Arbiters that have made a coherent decision & $\mathcal{A^+} \subset \mathcal{A}$ & $\mathsf{M}$ \\ + $\mathcal{A^-}$ & Subset of Arbiters that didn't participate or agreed with a coherent decision & \makecell{$\mathcal{A^-} = \mathcal{A} \setminus \mathcal{A^+}$ \\ $\mathcal{A} = \mathcal{A^+} \cup \mathcal{A^-}$ \\ $\mid\mathcal{A^+}\mid\; > \mu \mid\mathcal{A^-}\mid$} & $\mathsf{M}$ \\ + $n$ & Size of $\mathcal{A}$ (amount of Arbiters participating arbitration) & $3 \le n \ll\: \mid\mathbb{A}\:\mid$ & $\mathsf{P}$ \\ + $a$ & Standard stake of Arbiter locked in blockchain-based contract & $a \gg v$ & $\mathsf{P}$ \\ + \midrule +&\textbf{2. Cryptographic functions and arguments}\\ + $h, h_p$ & Hashed value $h$ and its \textit{preimage} $h_p$ initially selected and known only to the Worker. $h_p$ is used to encrypt the content of the data file. & $h = \mathtt{HASH}(h_p)$ & $W$ \\ + $t_1, t_2, t_3, t_4$ & \textit{Timelock} conditions ordered in such a way that smaller-indexed timelock precedes in time timelocks with larger indexes & $t_i \prec t_{i+n}$ & $\mathsf{P}$ \\ + $T^k_n$ & $k$-of-$n$ threshold multisignature & $n = \mid\mathcal{A}\mid, k = \lceil \mu n\rceil$ & $\mathsf{M}$ \\ + $d$ & \textit{Decision} taken by Arbiters, such as $\mid d=1$ is a pre-condition satisfying that the \textit{voting majority} of Arbiters $\mathcal{A^+}$ found Worker $W$ of being faulty. & $d \in \{0, 1\}$ & $\mathsf{M}$ \\ + \midrule +&\textbf{3. Meta-parameters}\\ + $\alpha$ & Fraction of stake taken from Worker's deposit $w$ in case of its timeout & $0 < \alpha \le 1$ & $\mathsf{P}$ \\ + $\beta$ & Fraction of stake taken from Verifier's deposit $v$ in case of its timeout & $0 < \beta \le 1$ & $\mathsf{P}$ \\ + $\gamma$ & Fraction of Worker's stake $w$ that is awarded to the Client as a compensation payment in case the Arbitation was not being able to reach a final decision & $0 < \gamma < \varepsilon$ & $\mathsf{P}$ \\ + $\delta$ & Fraction of stake $w$ taken from Worker $W$ if it was found faulty by the Verifier and didn't appeal & $\delta \le w-(1-\sigma)c$ & $\mathsf{P}$ \\ + $\varepsilon$ & Fraction of stakes that all participants ($W, V, \mathcal{A}$) are penalised in case of failed Arbitration (i.e. when $\mid\mathcal{A^+}\mid\; < \mu \mid\mathcal{A}\mid$) & $\gamma < \varepsilon \le 1$ & $\mathsf{P}$ \\ + $\mu$ & \textit{Arbiter voting majority}: threshold for Arbiters decision making & $\mu > 0.5$ & $\mathsf{P}$ \\ + $\sigma$ & Fraction of computation used by Worker in its \textit{computation argument} -- and subsequently verified by Verifier. It also defines the proportion of Client's payment $c$ that goes to Worker $W$ in case of successful computing (correspondingly, Verifier $V$ will receive reward of $1-\sigma c$) & $0 \le \sigma \le 1$ & $\mathsf{P}$ \\ + \bottomrule + \end{tabular} + \end{adjustbox} +\end{table} + +Multiparty state channel operates as a deterministic finite state machine with the following possible states (see also Fig.~\ref{fig:states}): +\begin{description} + \item[Setup] $\Lambda^\mathsf{S}$, performed by all parties interactively + \item[Computation] $\Lambda^\mathsf{C}$, performed by Worker + \item[Verification] $\Lambda^\mathsf{V}$, performed by Verifier + \item[Arbitration] $\Lambda^\mathsf{A}$ (optional phase), performed by special group of Arbiters + \item[Closed] $\Lambda^\mathsf{\times}$ with one of the settlement schemes published to the global state $\mathsf{G}$ (for settlement schemes see next section and Table~\ref{table:settlement}). The used settlement scheme is denoted in the subscript of the channel state, i.e. $\Lambda^\mathsf{\times}_\mathtt{3}$. +\end{description} + +State transitions are denoted by arrows with state transition conditions listed above the arrow. + +The normal state transition path is defined by $\Lambda^\mathsf{S} \xrightarrow{\mathsf{C}} \Lambda^\mathsf{C} \xrightarrow{\pi} \Lambda^\mathsf{V} \xrightarrow{r=1} \Lambda^\mathsf{\times}_\mathtt{3}$. + +\begin{figure}[h!] + \centering + \begin{adjustbox}{center}\digraph[scale=0.75]{statemachine}{ + rankdir=LR; rank=same; + { rank=same; + setup -> computing; + computing -> verification [label="proofs"]; + verification -> closed [label="verdict"]; } + setup -> closed [style=dotted, label="no agreement"]; + computing -> closed [style=dotted, label="timeout"]; + verification -> arbitration [label="appeal"]; + arbitration -> closed [label="decision"]; + }\end{adjustbox} + \caption{Possible channel states and transitions} + \label{fig:states} +\end{figure} + +\section{Settlement Algorithm} +\label{settlement} + +Prometheus \textit{settlement algorithm} provides economic incentives and settlement schemes with a provable Nash equilibrium for non-Byzantine behaviour of all of the rational participants. It is developed on top of incentive-based interactive settlement scheme for computation integrity proofs \cite{ibiss}. + +The algorithm runs in five phases (Fig.~\ref{fig:states}), each of which corresponds to particular state of the channel (Fig.~\ref{fig:states}) + +\begin{figure}[hp] + \centering + \includegraphics[height=1.5\columnwidth]{assets/settlementflow.pdf} + \caption{Prometheus settlement algorithm flowchart} + \caption*{\textit{Rectangles corresponds to contract phases and channel states; numbered exists (\#1, \#2 etc) corresponds to the settlement schemes from the Table~\ref{table:settlement}}} + \label{fig:settlementflow} +\end{figure} + +\subsection{Algorithm phases} +\label{phases} + +\subsubsection{Setup -- $\Lambda^\mathsf{S}$} +During the Setup phase, the Client node splits the actual computing task into \textit{batches} suitable for parallel processing in a distributed P2P network. + +For each batch, the client selects Worker and Verifier nodes on some open market or by auction (the actual selection is not part of this protocol). They agree upon the contract conditions (by committing to particular Prometheus contract $\mathsf{P}_i$) and computation reward $c$ in the process of off-chain P2P communications. + +To secure the actual contract Client, Worker and Verifier put $c, w, v$ under the \textit{computation contract} as described in section \ref{layers_contracts} and publish it to the blockchain. + +\subsubsection{Computation -- $\Lambda^\mathsf{C}$} +Worker node downloads all necessary data according to the contract and runs actual computation. Upon their completement the Worker encrypts the results with $h_p$ value, prepares PCP proof $\pi$ with one of the HLML-PCP schemes and updates the state of the channel with these data. The proof is constructed for some pre-defined portion of the data $\sigma$. + +If the phase is not completed within the pre-defined time, the Client or the Verifier has the right to terminate the contract and penalise the Worker (see settlement scheme \#1 in Fig.~\ref{fig:settlementflow} and Table~\ref{table:settlement}) + +\subsubsection{Verification -- $\Lambda^\mathsf{V}$} +Verifier node downloads source data with proofs $\pi$, checks them and reports \textit{verdict} $r$ into the channel. If the Worker found faulty, it has the right to either \textit{appeal} and start Arbitration procedure -- or \textit{admit the fault} by signing another transaction corresponding to settlement scheme \#4 (see Table~\ref{table:settlement}). In this case Worker will have a smaller penalty and we will prevent unnecessary computation load. + +If the phase is not completed within the pre-defined time, the Worker has the right to terminate the contract and penalise Verifier (see settlement scheme \#2 in Fig.~\ref{fig:settlementflow} and Table~\ref{table:settlement}) + +\subsubsection{Arbitration -- $\Lambda^\mathsf{A}$} +Arbiters $\mathcal{A}$ participating the Arbitration phase of the particular computing contract must be chosen from the global arbiters pool/set $\mathbb{A}$ (established well before the initiation of the computing contract) with some deterministic randomness procedure. This is required in order to prevent actors of spamming the Arbiters pool with their agents once the contract has reached Arbitration stage. + +Before become part of the $\mathcal{A}$ each of the selected Arbiters must confirm their participation via off-chain messaging. Jointly Arbiters constituting $\mathcal{A}$ sign their stakes into the computing contract with economically binding contract $\mathsf{A}$ as described in section \ref{layers_contracts} above. + +Arbiters reach decision in process of independent checks of $\pi$ proofs and P2P communications. To reach decision at least $\mu$ part of all Arbiters must come to the same coherent conclusion, where $\mu$ is a so-called \textit{voting majority} -- a parameter defined by Prometheus contract $\mathsf{P}_i$. + +If Arbiters are unable to reach a \textit{coherent decision} ($d=\emptyset$) with $\mu\%$ of votes, the computing contract defaults to payment scheme \#5 (Table~\ref{table:settlement}) where all of the contract participants (except of the Client) are penalised and the Client is compensated with some slice of their stake. + +If the coherent decision has being made, the contract completes with either \#6 or \#7 payment schemes (Table~\ref{table:settlement}) depending on the decision. In both schemes, the parties found faulty are penalised; and it the Worker computation results are found correct, the Worker receives its payment and the Client is provided with the results. + +\subsubsection{Payouts and Channel Closing -- $\Lambda^\mathsf{\times}$} +Depending on the completion of the phases 2, 3 and 4 there might be different ways of the final settlement, numbered on the Fig.~\ref{fig:settlementflow} from \#1 to \#7 and described below in the Table~\ref{table:settlement}. + +The channel is closed by publishing the singed branch of the transaction tree $\Upsilon$ from the channel scope into the global scope: $\Lambda^{\times} \xrightarrow{\Upsilon} \Gamma$. + +In cases when the computations by the Worker were proven to be correct (schemes \#2, \#3 and \#7) in order to take its reward the Worker must publish to the blockchain a spending transaction containing \textit{preimage} $h_p$ used for encrypting the resulting data payload~-- thus allowing Client to read its value and decrypt the data. + +\begin{table}[h!] + \caption{Settlement schemes} + \label{table:settlement} + \begin{adjustbox}{center} + \begin{tabular}{@{} l m{2.5cm} m{5cm} m{3cm} l @{}} + \toprule + ID & Scheme & Payout schedule & \makecell[l]{Precondition\\ $\mid$ postcondition\footnotemark[1],\footnotemark[2]} & Deposit redistribution\footnotemark[1] \\ + \midrule + \#1 & Worker timeout & Penalise Worker with a fraction of stake + & \makecell[l]{$\ldots \Lambda^\mathsf{C} \xrightarrow{\pi=\emptyset}$\\ $\mid\: t_3 \wedge (C \vee V)$ } + & $W: -\alpha w \rightarrow F$ \\ + \midrule + \#2 & Verifier timeout & Pay to Worker, penalise Verifier with a fraction of stake to Client + & \makecell[l]{$\ldots \Lambda^\mathsf{V} \xrightarrow{r=\emptyset}$\\ $\mid\: t_1 \wedge W \wedge h_p$ } + & \makecell[l]{$V: -\beta v \rightarrow C,$\\$C: -\sigma c \rightarrow W$} \\ + \midrule + \#3 & Normal payouts & Pay to Worker and Verifier + & \makecell[l]{$\ldots \Lambda^\mathsf{V} \xrightarrow{r=1}$\\ $\mid\: W \wedge h_p \wedge V$ } + & \makecell[l]{$C: -\sigma c \rightarrow W,$\\$C: -(1-\sigma) c \rightarrow V$} \\ + \midrule + \#4 & Faulty Worker, no appeal & Pay to Verifier from Worker stake, penalise Worker with a fraction of stake + & \makecell[l]{$\ldots \Lambda^\mathsf{V} \xrightarrow{r=0}$\\ $\mid\: t_2 \wedge V \wedge W$ } + & \makecell[l]{$W: -(1-\sigma)c \rightarrow V,$\\$W: -\delta w - (1 - \sigma)c$\\$\: \rightarrow F$} \\ + \midrule + \#5 & Failed Arbitration (no coherent decision) & Penalise all participants, compensate Client + & \makecell[l]{$\ldots \Lambda^\mathsf{V} \xrightarrow{r=0} \Lambda^\mathsf{A} \xrightarrow{d=\emptyset}$\\ $\mid\: t_4 \wedge C$ } + & \makecell[l]{$W: -\gamma w \rightarrow C,$\\ $W, V, \mathcal{A}:$\\$\: -\varepsilon(w+v+\sum a) \rightarrow F$} \\ + \midrule + \#6 & Arbitration proved Worker fault & Penalise Worker with full stake transferred to Arbiters, penalise faulty Arbiters + & \makecell[l]{$\ldots \Lambda^\mathsf{V} \xrightarrow{r=0} \Lambda^\mathsf{A} \xrightarrow{d=1}$\\ $\mid\: T^k_n(\mathcal{A^+})$ } + & \makecell[l]{$W: -w \rightarrow \mathcal{A^+},$ \\ $\forall A_i \in \mathcal{A^-}: -a_i \rightarrow F$} \\ + \midrule + \#7 & Arbitration proved Verifier fault & Pay to Worker, penalise Verifier with full stake transferred to Arbiters and Fund, penalise faulty Arbiters + & \makecell[l]{$\ldots \Lambda^\mathsf{V} \xrightarrow{r=0} \Lambda^\mathsf{A} \xrightarrow{d=0}$\\ $\mid\: T^k_n(\mathcal{A^+}) \wedge W \wedge h_p$ } + & \makecell[l]{$C: -c \rightarrow W$\\$V: -w \rightarrow \mathcal{A^+},$\\ $V: -(v-w) \rightarrow F,$\\ $\forall A_i \in \mathcal{A^-}: -a_i \rightarrow F$} \\ + \bottomrule + \end{tabular} + \end{adjustbox} +\end{table} +\footnotetext[1]{See Table~\ref{table:state} for the summary of the used state parameters} +\footnotetext[2]{The precondition is defined as a postfix of the state transition path in a part that is different from the normal state transition path $\Lambda^\mathsf{S} \xrightarrow{\mathsf{C}} \Lambda^\mathsf{C} \xrightarrow{\pi} \Lambda^\mathsf{V} \xrightarrow{r=1}$. +The postcondition is a set of conditions which have to be satisfied by the computing channel state $\mathsf{M}$.} + +\subsection{Game mechanics} +\label{game} + +To analyse all possible game strategies we need to pay special attention to the timelock postconditions $t_1-t_4$. In fact, their existence splits the whole game into separate timeslots, within which only designated players have ability to "move" (i.e. to sign some particular version of payout scheme or to wait/default/pass move by timeout to the players of the next timeslot). The analysis of this moves and timeslots is given in the Fig.~\ref{fig:strategies}. + +\begin{figure}[h!] + \centering + \begin{adjustbox}{center} + \includegraphics[width=1.2\columnwidth]{assets/strategies.pdf} + \end{adjustbox} + \caption{Possible game strategies} + \label{fig:strategies} +\end{figure} + +At the start of the game (setup phase), a complete set of seven possible payout transactions spending the deposited reward and stakes is created. Each of the transactions is pre-signed by all actors that are \textbf{not} present on the path leading to the settlement scheme corresponding to the transaction. Through the process of game each participant decides which transaction to sign and, by doing so, it adds to the post-condition criteria for the corresponding settlement scheme from the Table~\ref{table:settlement} -- until the completion and publication of one of them. Each signature on the path is equal to the change in the state of the channel and each game end-leaf (exit) is an implementation of some particular payout scheme. + +Thus, by analysing both finite state machine and game theory models we have shown deterministic nature of the settlement algorithm leading to the Byzantine tolerance for the rational set of participants resulting in verified computation and proper reward distribution. + +\section{Transaction model} +\label{transaction_model} + +\subsection{Prometheus contracts} +\label{p_contracts} + +Prometheus contracts $\{\mathsf{P}_i\}$ are defined in the global scope using on-chain transactions $\{ \mathsf{T}_{\mathsf{P}_i} \} \mid\Gamma^\Omega$. Each transaction stores a tuple of contract parameters $\mathsf{P} = \langle w, v, n, a, F, \alpha, \beta, \gamma, \delta, \varepsilon, \mu, \sigma, t_1, t_2, t_3, t_4 \rangle$ in explicit form as part of its single \texttt{OP\_RETURN} output. Explicit storage is required for faster achievement of global state $\mathsf{G}$ syncronisation without requirements to run multiple P2P requests and data keeping. The cost of on-chain storage is low, since each of the Prometheus contracts will define some set of parameters matching well-known best practice and there are just few possible options that can satisfy conditions for the Nash equilibrium. + +Prometheus contracts are committed to by arbitration offer and computational contract originating transactions by their transaction hash (\texttt{txid}, see sections below for the details). + +\subsection{Arbitration offers} +\label{o_contracts} + +Arbitration offers are contracts created by the parties wishing to participate in the future Arbitrations started by Worker appeals under computational contracts (see section Arbitration below). + +Arbitration offer $\mathsf{O}_i$ by a party $A_i$ consists of a commitment to a particular Prometheus contract $\mathsf{P}_x$ and stake proof $a_p$ that corresponds by its value to the parameter $a$ withing the committed contract: $\mathsf{O} = \langle \mathsf{P}_x, a_p \rangle$. This information is packed into a transaction $\mathsf{T_O}$ with $a_p$ amount stored under HTLC P2SH contract in \texttt{scriptPubKey} of its first output and with second output referencing corresponding $\mathsf{T}_{\mathsf{P}_x}$ \texttt{txid} via \texttt{OP\_RETURN} code (see Fig.~\ref{fig:txo}). HTLC type of the contract for the stake lock is required to protect from lateral stake spending during the \textit{computational contract} execution (see \ref{lifecycle} below) and from committing to multiple \textit{computational contracts}. + +The purposes of such transaction is to: +\begin{itemize}[topsep=0pt] + \tightlist + \item Put information into the global state $\mathsf{G}$ about party willing to arbitrate under some specific contract conditions + \item Prove availability of the funds required for creation of arbitration stake + \item Prove time of the arbitration offer (important for achieving Nash equilibrium for the Prometheus settlement algorithms) +\end{itemize} + +\begin{figure}[h!] + \centering + \includegraphics[scale=0.75]{assets/txo.pdf} + \caption{Structure of the arbitration offer commitment transaction $\mathsf{T_O}$} + \label{fig:txo} +\end{figure} + +Only well-formed transactions with unspent output are taken as a part of global state of the system: $(\mathbb{A} \equiv \{ \mathsf{T}_{\mathsf{O}_i} \} \equiv \{ \mathsf{O}_i \mid \Gamma^\Omega \}) \subset \mathsf{P} \subset \mathsf{G}$. + +\subsection{Computational contracts and stage channel lifecycle} +\label{lifecycle} + +\subsubsection{Setup phase overview} + +Channel $\mathsf{M}$ is initiated during its setup phase $\mathsf{M}\mid\Lambda^\mathsf{S}$ off-chain by establishing \textit{computational contract} $\mathsf{C}$ -- multi-party economically-binding agreement between the Client, Worker node and Verifier node. The agreement is established via P2P communication protocol, described in the next chapter of the specification. +Initially, contract in its off-chain version $\mathsf{C}\mid\Lambda^\mathsf{S}$ consists of some selected Prometheus contract $\mathsf{P}_x$, defining main parameters for the contract execution, and initial set of unpublished transactions, namely \textit{commitment transaction} $\mathsf{T_0}$, \textit{arbitration commitment transaction} $\mathsf{T_A}$ and \textit{initial set of settlement transactions} consisting of 8 members $\mathsf{T_{1\dots 8}}$: $\mathsf{C}\mid\Lambda^\mathsf{S} = \langle \mathsf{T_0}, \mathsf{T^\star_A}, \{ \mathsf{T^\star_{1\dots 8}} \} \rangle$ ($\mathsf{T}^\star$ denotes partially-signed transaction). All of these transactions are created and signed by utilising Prometheus P2P protocol. + +\begin{description} + \item[Commitment transaction (notation $\mathsf{T_0}$, txid \texttt{Tx0})] + locks deposit consisting of reward $c$ from the Client and stakes $w, v$ from the Worker and Verifier nodes stored under a single output (see Fig.~\ref{fig:txtree} for the details on transaction structure). As the second output, the transaction contains \texttt{OP\_RETURN} \textttt{scriptPubKey} referencing the hash of the corresponding Prometheus contract $\mathsf{P}$ transaction the participants are committing to. + + \item[Arbitration commitment transaction (notation $\mathsf{T^\star_A}$, txid \texttt{TxA})] + serves the purpose of pre-agreed dispute resolution with economically-binding commitments from all of the participants. Thus, it is fully-signed during the setup phase, and the failure of creating such transaction implies the failure for establishing computational contract: the channel will proceed to the closed state not being published into the global scope. Due to its keystone importance the transaction is created and signed with a special protocol (ceremony), described in the following section. + + \item[Settlement transactions (notation $\{ \mathsf{T}^\star_i \mid 1 \le i \le 8 \}$, txid \texttt{Tx1-Tx8})] + partially-signed transactions, covering all settlement schemes outside Arbitration. They remain unpublished during the channel existence, i.e. reside off-chain in the channel scope as a part of multiparty channel state $\mathsf{T}^\star_{1\dots5} \in \mathsf{M}(\mathsf{C}_i) \mid \Lambda$. Details on these transactions will be covered in the part related to the channel lifecycle. + +\end{description} + +All prepared transactions are organised into partially-signed transaction tree $\Upsilon$ originating from the \textit{commitment transaction} (Fig..~\ref{fig:txtree}). + +\begin{figure}[h!] + \centering + \includegraphics[width=0.9\columnwidth]{assets/txtree.pdf} + \caption{Details on the structure of the transactions inside $\Upsilon$ transaction tree} + \caption*{\textit{To be brief, we have simplified some details in the transaction fields: +amounts with superscripts $x^-$ and $x^+$ signify that the recipient will receive original deposit/stake $x$ either in decreased (slashed) or increased (with some additional payment) form. The exact sizes of the slashes and rewards are provided by the Table~\ref{table:settlement}; +\~ tildes stands for "amount might vary", please check the Table~\ref{table:settlement} for specific amounts for different settlement schemes corresponding to the given transaction ids according to the Table~\ref{table:tx}}} + \label{fig:txtree} +\end{figure} + +\subsubsection{Arbitration commitment ceremony} +\label{ceremony} + +The ceremony must guarantee: +\begin{enumerate} + \item Random selection of Arbiters, that were existed before the creation of the \textit{computation contract} and have committed with some stake. This prevents occupation of the network with pre-registered Arbiters and excludes possibility of the participants to change the existing Arbiters depending on the strategy of contract execution. + \item Commitment from the selected Arbiters to participate in the contract execution. + \item Absence of censorship from one party or any kind of coalition from the contract participants. +\end{enumerate} + +These criteria are satisfied by the following procedure: +\begin{enumerate} + \item A global set of arbiters $\mathbb{A} \mid \Gamma^\Omega$ is known in global on-chain scope before the initiation of the contract and fixed with a snapshot for the full time of contract execution. + + \item All of its participants of $\mathbb{A}$ have committed to the same parameters of the contract (i.e. the same Prometheus contract $\mathsf{P}$) and have locked their stake with a proper \textit{arbitration offer} transaction: $\forall A_i \in \mathbb{A} \mid \mathsf{P}_x: \exists \mathsf{O}_i \mid \Gamma^\Omega = \langle \mathsf{P}_x \dots \rangle$. For details on arbitration offers see the corresponding section in the current chapter. + + \item Deterministic random oracle is utilized by all contract participants to sample $\mathbb{A}$. The simplest form of the oracle is a hash function taken from concatenated string of all \texttt{txid} used by $\mathbb{A}$ participants in their arbitration offer transactions. + + \item Each of $C, V, W$ does the following in an independent manner: + \begin{itemize} + \item Sample set $\mathbb{A}$ in an iterative manner by sending requests to the P2P network and collecting responses of those arbiters who will to participate in the contract. This continues until each of the participants will make a list $\mathcal{A}, |\mathcal{A}|=\mu$ of the parties that have responded to the request. + + \item Present to others participants in $\Lambda^\mathsf{S}$ his own view on $\mathcal{A}$, namely $\mathcal{A}^W, \mathcal{A}^V, \mathcal{A}^C$. + + \item Compute $\mathcal{A}' = \mathcal{A}^W \cup \mathcal{A}^V \cup \mathcal{A}^C$ and reorder it according to the original random sampling order, select subset $\mathcal{A} \subset \mathcal{A}': \mathcal{A} = \{ A_i \in \mathcal{A}': i \le \mu \}$. + + \item Prepare $\mathsf{T^\star_A}$ with all necessary fields (see Fig.~\ref{fig:txtree}) including all of the Arbiters from $\mathcal{A}$ and sign $\mathsf{T^\star_A}$ with each of the $\mathcal{A}$ participants (via P2P communications) + + \item Run Shamir's secret sharing protocol extended with collective secret generation procedure as described in \cite{typhon} across all selected arbiters $\mathcal{A}$ + \end{itemize} + + \item If there were no Byzantine parties, at the end of this ceremony all of participants will have the same version of $\mathsf{T^\star_A}$. + + \item If the version of $\mathsf{T^\star_A}$ does not match between the participants, the channel and computation contract must be closed via $\Lambda^\mathsf{S} \rightarrow \Lambda^\times$ procedure. The Client may try to set up another computing contract by opening new channel and running the same procedure with different $W$ and $V$. + +\end{enumerate} + +\subsubsection{Completion of channel creation} +Transactions, constituting $\Upsilon$ of \textit{computational contract} $\mathsf{C}$ during the setup phase $\Lambda^\mathsf{S}$ must be (partially)signed in the foolowing order: $\mathsf{T^\star_A} \parallel \mathsf{T^\star_6} \prec \mathsf{T^\star_{7,8}} \prec \mathsf{T^\star_{1\dots 5}} \prec \mathsf{T_0}$. This order is important so that inability to successfully accomplish the arbitration commitment ceremony will result in proper channel termination without option for possible Byzantine parties to publish the rest of prepared transactions on-chain and failure channel closure. + +Upon the completion of the $\Upsilon$ construction the original fully-signed \textit{commitment transaction} must be brought into the global space by the Client, putting its last signature upon it. Following publishing of the commitment transaction and its inclusion into the blockchain the transaction becomes \textit{computing contract} as it is seen by everybody in the global on-chain scope $\mathsf{T}_0 \equiv \mathsf{C} \mid \Gamma^\Omega = \langle \mathsf{P}_i, C, W, V, c, w_p, v_p\rangle$ (pls. note that the payload data part of the $\mathsf{C}$ is kept off-chain within the channel scope $\Lambda$, being known only to channel participants). The transaction tree $\Upsilon$ from now till the end of computation and validation stages persists in the state shown on Fig.~\ref{fig:upsilon}. + +\begin{figure}[h!] + \centering + \includegraphics[width=0.75\columnwidth]{assets/upsilon.pdf} + \caption{Transaction tree $\Upsilon\mid(\Lambda^{\mathsf{C}\vee\mathsf{V}} \cup \Gamma)$ during computation and verification channel states} + \caption*{\textit{On-chain/published transactions (thus, defined in $\Gamma^\Omega$ scope) are shown in bold, partially-signed transactions -- with dotted line.}} + \label{fig:upsilon} +\end{figure} + +\subsubsection{Normal channel operation and closure} +Prometheus settlement algorithm is implemented utilising partially-signed transactions from the $\Upsilon$ (Fig.~\ref{fig:upsilon}), prepared at the setup phase by all involved parties. They are consist of two subsets: \textit{pre-arbitration} and \textit{post-arbitration transactions}. + +Pre-arbitration transactions are specified in the Table~\ref{table:tx} and Fig.~\ref{fig:txtree}-\ref{fig:upsilon} by their ids \texttt{Tx1-Tx5}. These transactions cover all settlement schemes outside Arbitration and remain unpublished, i.e. residing off-chain in the channel scope as a part of multiparty channel state $\mathsf{T}_{1\dots5} \in \mathsf{M}(\mathsf{C}_i) \mid \Lambda$. Upon corresponding state transitions from the Fig.~\ref{fig:states} they will be signed by the proper party (see "Post-signed" column in the Table~\ref{table:tx}), and published into the global scope to be included on-chain. This completes computing contract $\mathsf{C}$, invalidates local scope $\Lambda$ and closes the state channel $\mathsf{M}(\mathsf{C})$ according to the corresponding settlement scheme from the Table~\ref{table:settlement}: + +$(\mathsf{T}^\star_j\mid\Lambda) \xrightarrow{C \vee W \vee V} (\mathsf{T}_j \mid \Gamma^\Omega);\enspace \exists j: 1 \le j \le 5\quad \Longrightarrow \quad \mathsf{M}(\mathsf{C}) \mid (\Lambda^{\mathsf{S}\vee\mathsf{C}\vee\mathsf{V}} \rightarrow \Lambda^\times_{\mathsf{1\dots4}})$ + +For the settlement schemes that have to reveal computation results to the client (\#2 and \#3), the signing Worker node must additionally include into the signed transaction \textit{preimage} $h_p$ that was originally used for the results encryption. This is required by unlocking script of \texttt{Tx0} which contains hash-lock conditions in branches related to these settlement schemes (see below section on the unlocking scripts details). + +\begin{table}[h!] + \caption{Settlement transactions and their finalisation conditions} + \label{table:tx} + \begin{adjustbox}{center} + \begin{tabular}{@{} r r c c c c @{}} + \toprule + Id & Payout Schedule & Pre-signed & Post-signed & Hashlock & nTimelock\\ + \midrule + \texttt{Tx1} & \#1 & $W, C$ & $V$ & - & $t_3$ \\ + \texttt{Tx2} & \#1 & $W, V$ & $C$ & - & $t_3$ \\ + \texttt{Tx3} & \#2 & $V, C$ & $W$ & $h_p$ & $t_2$ \\ + \texttt{Tx4} & \#3 & $C, W$ & $V$ & $h_p$ & - \\ + \texttt{Tx5} & \#4 & $C, W$ & $V$ & - & $t_1$ \\ + \texttt{TxA} & \#5-7 & $C, W, A$ & $V$ & $k_p$ & - \\ + \texttt{Tx6} & \#5 & $V, W, A$ & $C$ & - & $t_4$ \\ + \texttt{Tx7} & \#6 & $C, W, V$ & - & $T^k_n(\mathcal{A^+})$ & - \\ + \texttt{Tx8} & \#7 & $C, W, V$ & - & $T^k_n(\mathcal{A^+})$, $h_p$ & w \\ + \bottomrule + \end{tabular} + \end{adjustbox} +\end{table} + +\subsubsection{Arbitration} + +In order to transition the channel state into the Arbitration, $\mathsf{M}(\mathsf{C}) \mid \Lambda^{\mathsf{C}} \rightarrow \Lambda^{\mathsf{A}}$, Worker must submit appeal by including its \textit{arbitration-unlocking preimage} $k_p$ into \texttt{scriptSig} of \texttt{TxA} (which is already signed by the Verifier as part of its \textit{verdict} on the Worker fault) and publishing the transaction: + +$(\mathsf{T}^\star_A\mid\Lambda^\mathsf{V}) \xrightarrow{k_p} (\mathsf{T}_A\mid\Gamma^\Omega) \quad \Longrightarrow \quad\mathsf{M}(\mathsf{C}) \mid (\Lambda^{\mathsf{V}} \xrightarrow{\mathtt{appeal}} \Lambda^{\mathsf{A}})$. + +This signals to the arbiters in $\mathcal{A}$ to initiate their verification procedure on the data from $\mathsf{M}$, mainly PCP proofs $\pi$. + +Arbitration is completed in one of the ways defined in the previous chapter. Each of them has a corresponding pre-signed transaction \texttt{Tx6-8} (Table~\ref{table:tx}), which can be finalised by the interested party and published into the global scope, which will close the channel and complete settlement: + +$(\mathsf{T}^\star_j\mid\Lambda) \xrightarrow{C \vee \mathcal{A}^+} (\mathsf{T}_j \mid \Gamma^\Omega);\enspace \exists j: 6 \le j \le 8\quad \Longrightarrow \quad \mathsf{M}(\mathsf{C}) \mid (\Lambda^{\mathsf{A}} \rightarrow \Lambda^\times_{\mathsf{5\dots7}})$ + +Arbiters that came to the \textit{coherent decision} sign transactions \texttt{Tx7-Tx8} by revealing their parts of the collectively shared secret with a threshold signature $T^k_n$, the corresponding revealed secret is a \textit{preimage} for the hashlock condition in \texttt{scriptSig} part of the transactions. + +\section{Conclusion} + +In the present work we have formally defined Prometheus: a protocol for running high-load \& machine learning computing inside a trustless decentralized environment in Byzantine-tolerant way, utilizing zero knowledge computation integrity arguments, economic incentives and p2p networking. The paper have covered protocol design rationale, architecture, formal protocol definition and specified protocol operations with demonstrable Nash equilibrium, as well as transaction model for implementation with Bitcoin blockchain and script functionality. + +\nocite{*} +\bibliographystyle{abbrv} +\bibliography{references} + +\end{document} diff --git a/references.bib b/references.bib new file mode 100644 index 0000000..8d21f36 --- /dev/null +++ b/references.bib @@ -0,0 +1,243 @@ +@article{bitcoin, + title={Bitcoin: A peer-to-peer electronic cash system}, + author={Satoshi Nakamoto}, + howpublished = "\url{https://bitcoin.org/bitcoin.pdf}", + year={2008} +} + +@article{sidechains, + title={Enabling Blockchain Innovations with Pegged Sidechains}, + author={Adam Back and Matt Corallo and Luke Dashjr and Mark Friedenbach and Gregory Maxwell and Andrew Miller and Andrew Poelstra and Jorge Timon and Pieter Wuille}, + howpublished = "\url{https://blockstream.com/technology/sidechains.pdf}", + year={2014} +} + +@misc{ibiss, + title={Incentive-based interactive settlement scheme for computational integrity proofs (IBISS-CIP)}, + author={Maxim Orlovsky and Sabina Sachtachtinskagia}, + howpublished="\url{https://github.com/pandoracore/ibiss-spec/blob/master/ibiss-spec.pdf}", + year={2019}, + organization={Pandora Core AG} +} + +@misc{typhon, + title={Typhon: trustless Bitcoin sidechain protocol}, + author={Maxim Orlovsky}, + howpublished = "\url{https://github.com/dr-orlovsky/typhon-spec}", + year={2019}, + organization={Pandora Core AG} +} + +@article{ipfs, + title={IPFS - Content Addressed, Versioned, P2P File System}, + author={Juan Benet}, + howpublished = "\url{https://ipfs.io/ipfs/QmR7GSQM93Cx5eAg6a6yRzNde1FQv7uL6X1o4k7zrJa3LX/ipfs.draft3.pdf}", + year={2016} +} + +@article{AFS, + title={Scale and performance in a distributed file system}, + author={Howard, John H and Kazar, Michael L and Menees, Sherri G and Nichols, David A and Satyanarayanan, Mahadev and Sidebotham, Robert N and West, Michael J}, + journal={ACM Transactions on Computer Systems (TOCS)}, + volume={6}, + number={1}, + pages={51--81}, + year={1988}, + publisher={ACM} +} + +@inproceedings{bittorrent, + title={Incentives build robustness in BitTorrent}, + author={Cohen, Bram}, + booktitle={Workshop on Economics of Peer-to-Peer systems}, + volume={6}, + pages={68--72}, + year={2003} +} + +@misc{tor, + title={Tor: The Second-Generation Onion Router}, + author={Roger Dingledine and Nick Mathewson and Paul Syverson}, + howpublished= "\url{https://svn.torproject.org/svn/projects/design-paper/tor-design.pdf}" +} + +@article{cloudcomputing, + author="Zhang, Qi + and Cheng, Lu + and Boutaba, Raouf", + title="Cloud computing: state-of-the-art and research challenges", + journal="Journal of Internet Services and Applications", + year="2010", + month="May", + day="01", + volume="1", + number="1", + pages="7--18", + issn="1869-0238", + doi="10.1007/s13174-010-0007-6", + url="https://doi.org/10.1007/s13174-010-0007-6" +} + +@inproceedings{anonymity, + author="Acquisti, Alessandro + and Dingledine, Roger + and Syverson, Paul", + editor="Wright, Rebecca N.", + title="On the Economics of Anonymity", + booktitle="Financial Cryptography", + year="2003", + publisher="Springer Berlin Heidelberg", + address="Berlin, Heidelberg", + pages="84--102", + isbn="978-3-540-45126-6" +} + +@inproceedings{pcpibm, + author="Gennaro, Rosario + and Gentry, Craig + and Parno, Bryan", + editor="Rabin, Tal", + title="Non-interactive Verifiable Computing: Outsourcing Computation to Untrusted Workers", + booktitle="Advances in Cryptology -- CRYPTO 2010", + year="2010", + publisher="Springer Berlin Heidelberg", + address="Berlin, Heidelberg", + pages="465--482", + isbn="978-3-642-14623-7" +} + +@article{cc-censorship1, + author = { Burke T. Ward and Janice C. Sipior }, + title = {The Internet Jurisdiction Risk of Cloud Computing}, + journal = {Information Systems Management}, + volume = {27}, + number = {4}, + pages = {334-339}, + year = {2010}, + publisher = {Taylor & Francis}, + doi = {10.1080/10580530.2010.514248}, + URL = { + https://doi.org/10.1080/10580530.2010.514248 + }, + eprint = { + https://doi.org/10.1080/10580530.2010.514248 + } +} + +@article{cc-censorship2, + title = "Privacy and security issues in cloud computing: The role of institutions and institutional evolution", + journal = "Telecommunications Policy", + volume = "37", + number = "4", + pages = "372 - 386", + year = "2013", + issn = "0308-5961", + doi = "https://doi.org/10.1016/j.telpol.2012.04.011", + url = "http://www.sciencedirect.com/science/article/pii/S0308596112000717", + author = "Nir Kshetri" +} + +@article{truebit, + title={A scalable verification solution for blockchains}, + author={Jason Teutsch and Christian Reitwießner}, + howpublished="\url{https://people.cs.uchicago.edu/~teutsch/papers/truebit.pdf}", + year={2017} +} + +@misc{freeai, + title={The \#FreeAI Manifesto}, + howpublished = "\url{https://manifesto.ai/}", + year={2018} +} + +@misc{webassembly, + title={WebAssembly}, + howpublished = "\url{http://webassembly.org/}", + year={2016} +} + +@misc{pcp, + author = {Eli Ben-Sasson and Alessandro Chiesa and Nicholas Spooner}, + title = {Interactive Oracle Proofs}, + howpublished = {Cryptology ePrint Archive, Report 2016/116}, + year = {2016}, + note = {\url{https://eprint.iacr.org/2016/116}}, +} + +@inproceedings{gnunet, + title={Gnunet -- a truly anonymous networking infrastructure}, + author={Bennett, Krista and Grothoff, Christian and Horozov, Tzvetan and Patrascu, Ioana and Stef, Tiberiu}, + booktitle={In: Proc. Privacy Enhancing Technologies Workshop (PET}, + year={2002}, + organization={Citeseer} +} + +@misc{libp2p, + title={The specs for libp2p and associated submodules.}, + howpublished = "\url{https://github.com/libp2p/specs}" +} + +@inproceedings{kademlia, + title={Kademlia: A Peer-to-peer Information System Based on the XOR Metric}, + author={Maymounkov, Petar and Mazi\`eres, David}, + booktitle={IPTPS '01 Revised Papers from the First International Workshop on Peer-to-Peer Systems}, + pages={53-65}, + year={2002} +} + +@inproceedings{Badertscher, + author="Badertscher, Christian + and Garay, Juan + and Maurer, Ueli + and Tschudi, Daniel + and Zikas, Vassilis", + editor="Nielsen, Jesper Buus + and Rijmen, Vincent", + title="But Why Does It Work? A Rational Protocol Design Treatment of Bitcoin", + booktitle="Advances in Cryptology -- EUROCRYPT 2018 ", + year="2018", + publisher="Springer International Publishing", + address="Cham", + pages="34--65", + isbn="978-3-319-78375-8" +} + +@article{Houy, + title={The Bitcoin Mining Game}, + author={Nicolas Houy}, + journal={Ledger}, + year={2016}, + volume={1}, + pages={53-68} +} + +@book{gametheory, + title = {Game Theory}, + year = {1991}, + publisher = {MIT Press}, + organization = {MIT Press}, + address = {Cambridge, MA}, + author = {Drew Fudenberg and Jean Tirole} +} + +@inproceedings{Kumaresan, + title={How to Use Bitcoin to Incentivize Correct Computations}, + author={Ranjit Kumaresan and Iddo Bentov}, + booktitle={ACM Conference on Computer and Communications Security}, + year={2014} +} + +@book{Laffont, + author = { Laffont, Jean-Jacques, and Martimort, David. }, + title = { The theory of incentives: the principal-agent model }, + isbn = { 0691091838 0691091846 }, + publisher = { Princeton University Press Princeton, N.J. ; Oxford }, + pages = { xii, 421 p. : }, + year = { 2002 }, + type = { Book }, + url = { http://www.loc.gov/catdir/toc/prin031/2001051039.html http://www.loc.gov/catdir/toc/prin031/2001051039.html }, + language = { English }, + subjects = { Economics.; Incentives in industry. }, + life-dates = { 2002 - }, + catalogue-url = { https://nla.gov.au/nla.cat-vn1556459 }, +} diff --git a/tikz-uml.sty b/tikz-uml.sty new file mode 100644 index 0000000..eacab58 --- /dev/null +++ b/tikz-uml.sty @@ -0,0 +1,5376 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Start of tikz-uml.sty +% +% Some macros for UML Diagrams. +% Home page of project : +% Author: Nicolas Kielbasiewicz +% Style from : +% Fixed by Nicolas Kielbasiewicz (nicolas.kielbasiewicz@ensta-paristech.fr) in march 2016 to compile with pgf 3.00 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\NeedsTeXFormat{LaTeX2e}[1995/12/01]% +\ProvidesPackage{tikz-uml}[2011/01/26]% +% +\RequirePackage{ifthen}% +\RequirePackage{tikz}% +\RequirePackage{xstring}% +\RequirePackage{calc}% +\RequirePackage{pgfopts}% +\usetikzlibrary{backgrounds,arrows,shapes,fit,shadows,decorations.markings}% +% +\def\tikzumlPackageLayersNum{3}% +\pgfkeys{/tikzuml/options/.cd, packageLayers/.initial=3}% +\pgfkeys{/tikzuml/options/.cd, packageLayers/.store in=\tikzumlPackageLayersNum}% +\def\tikzumlStateLayersNum{3}% +\pgfkeys{/tikzuml/options/.cd, stateLayers/.initial=3}% +\pgfkeys{/tikzuml/options/.cd, stateLayers/.store in=\tikzumlStateLayersNum}% +\def\tikzumlFragmentLayersNum{3}% +\pgfkeys{/tikzuml/options/.cd, fragmentLayers/.initial=3}% +\pgfkeys{/tikzuml/options/.cd, fragmentLayers/.store in=\tikzumlFragmentLayersNum}% +\def\tikzumlComponentLayersNum{3}% +\pgfkeys{/tikzuml/options/.cd, componentLayers/.initial=3}% +\pgfkeys{/tikzuml/options/.cd, componentLayers/.store in=\tikzumlComponentLayersNum}% +% +\ProcessPgfOptions{/tikzuml/options}% +% +\def\pgfsetlayersArg{background}% +\pgfdeclarelayer{background}% +\newcounter{tikzumlPackageLayers}% +\loop \pgfdeclarelayer{package\thetikzumlPackageLayers}% + \xdef\pgfsetlayersArg{\pgfsetlayersArg,package\thetikzumlPackageLayers}% + \ifnum\tikzumlPackageLayersNum>\thetikzumlPackageLayers% + \stepcounter{tikzumlPackageLayers}% +\repeat% +% +\newcounter{tikzumlFragmentLayers}% +\loop \pgfdeclarelayer{fragment\thetikzumlFragmentLayers}% + \xdef\pgfsetlayersArg{\pgfsetlayersArg,fragment\thetikzumlFragmentLayers}% + \ifnum\tikzumlFragmentLayersNum>\thetikzumlFragmentLayers% + \stepcounter{tikzumlFragmentLayers}% +\repeat% +% +\newcounter{tikzumlStateLayers}% +\loop \pgfdeclarelayer{state\thetikzumlStateLayers}% + \xdef\pgfsetlayersArg{\pgfsetlayersArg,state\thetikzumlStateLayers}% + \ifnum\tikzumlStateLayersNum>\thetikzumlStateLayers% + \stepcounter{tikzumlStateLayers}% +\repeat% +% +\newcounter{tikzumlComponentLayers}% +\loop \pgfdeclarelayer{component\thetikzumlComponentLayers}% + \xdef\pgfsetlayersArg{\pgfsetlayersArg,component\thetikzumlComponentLayers}% + \ifnum\tikzumlComponentLayersNum>\thetikzumlComponentLayers% + \stepcounter{tikzumlComponentLayers}% +\repeat% +% +\pgfdeclarelayer{lifelines}% +\pgfdeclarelayer{activity}% +\pgfdeclarelayer{connections}% +\xdef\pgfsetlayersArg{\pgfsetlayersArg,lifelines,activity,connections,main}% +\pgfsetlayers{\pgfsetlayersArg}% +% +\pgfkeys{/tikzuml/.cd, text/.initial=black, draw/.initial=black, font/.initial=\small,% + x/.initial=0, y/.initial=0,% + package type/.initial=tikzumlEmpty, fill package/.initial=blue!20,% + class width/.initial=10ex, simple interface width/.initial=4ex, class type/.initial=class, fill class/.initial=yellow!20, fill template/.initial=yellow!2,% + narynode width/.initial=6ex,% + relation geometry/.initial=--, relation angle1/.initial=-30, relation angle2/.initial=30, relation loopsize/.initial=3em, relation weight/.initial=0.5, relation pos1/.initial=0.2, relation pos2/.initial=0.8, relation pos stereo/.initial=0.5,% + note width/.initial=3cm, fill note/.initial=green!20,% + fill system/.initial=white,% + fill usecase/.initial=blue!20,% + actor below/.initial=0.5cm,% + state join width/.initial=3ex,% + state decision width/.initial=3ex,% + state initial width/.initial=5ex,% + state final width/.initial=5.5ex,% + state enter width/.initial=5ex,% + state exit width/.initial=5ex,% + state end width/.initial=5ex,% + state history width/.initial=5ex,% + state deep history width/.initial=5ex,% + state width/.initial=8ex, fill state/.initial=yellow!20,% + object stereo/.initial=object, fill object/.initial=yellow!20,% + call dt/.initial=tikzumlEmpty, call padding/.initial=2, call type/.initial=synchron, fill call/.initial=white,% + fragment type/.initial=opt, fragment inner xsep/.initial=1, fragment inner ysep/.initial=1, fill fragment/.initial= none,% + create call dt/.initial=4,% + component width/.initial=8ex, fill component/.initial= yellow!20,% + required interface distance/.initial=2.5cm, required interface width/.initial=1em, required interface padding/.initial=1cm,% + provided interface distance/.initial=3cm, provided interface width/.initial=1em, provided interface padding/.initial=1cm,% + port width/.initial=1ex, fill port/.initial= yellow!20,% + fill assembly connector/.initial= white,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in tikzuml global, invalid option \keyname}% + }% +}% +% +\pgfkeys{/tikzuml/.cd, text/.get=\tikzumlDefaultTextColor, draw/.get=\tikzumlDefaultDrawColor, font/.get=\tikzumlDefaultFont,% + x/.get=\tikzumlDefaultX, y/.get=\tikzumlDefaultY,% + package type/.get=\tikzumlPackageDefaultType, fill package/.get=\tikzumlPackageDefaultFillColor,% + class width/.get=\tikzumlClassDefaultWidth, simple interface width/.get=\tikzumlSimpleInterfaceDefaultWidth, class type/.get=\tikzumlClassDefaultType, fill class/.get=\tikzumlClassDefaultFillColor, fill template/.get=\tikzumlClassTemplateFillColorDefaultFillColor,% + narynode width/.get=\tikzumlNaryNodeDefaultWidth,% + relation geometry/.get=\tikzumlRelationDefaultGeometry, relation angle1/.get=\tikzumlRelationDefaultAngleO, relation angle2/.get=\tikzumlRelationDefaultAngleT, relation loopsize/.get=\tikzumlRelationDefaultLoopSize, relation weight/.get=\tikzumlRelationDefaultWeight, relation pos1/.get=\tikzumlRelationDefaultPosO, relation pos2/.get=\tikzumlRelationDefaultPosT, relation pos stereo/.get=\tikzumlRelationDefaultPosStereo,% + note width/.get=\tikzumlNoteDefaultWidth, fill note/.get=\tikzumlNoteDefaultFillColor,% + fill system/.get=\tikzumlSystemDefaultFillColor,% + fill usecase/.get=\tikzumlUseCaseDefaultFillColor,% + actor below/.get=\tikzumlActorDefaultBelow,% + state join width/.get=\tikzumlStateJoinDefaultWidth,% + state decision width/.get=\tikzumlStateDecisionDefaultWidth,% + state initial width/.get=\tikzumlStateInitialDefaultWidth,% + state final width/.get=\tikzumlStateFinalDefaultWidth,% + state enter width/.get=\tikzumlStateEnterDefaultWidth,% + state exit width/.get=\tikzumlStateExitDefaultWidth,% + state end width/.get=\tikzumlStateEndDefaultWidth,% + state history width/.get=\tikzumlStateHistoryDefaultWidth,% + state deep history width/.get=\tikzumlStateDeepHistoryDefaultWidth,% + state width/.get=\tikzumlStateDefaultWidth, fill state/.get=\tikzumlStateDefaultFillColor,% + object stereo/.get=\tikzumlObjectDefaultStereo, fill object/.get=\tikzumlObjectDefaultFillColor,% + call dt/.get=\tikzumlCallDefaultDT, call padding/.get=\tikzumlCallDefaultPadding, call type/.get=\tikzumlCallDefaultType, fill call/.get=\tikzumlCallDefaultFillColor,% + fragment type/.get=\tikzumlFragmentDefaultType, fragment inner xsep/.get=\tikzumlFragmentDefaultXSep, fragment inner ysep/.get=\tikzumlFragmentDefaultYSep, fill fragment/.get=\tikzumlFragmentDefaultFillColor,% + create call dt/.get=\tikzumlCreateCallDefaultDT,% + component width/.get=\tikzumlComponentDefaultWidth, fill component/.get=\tikzumlComponentDefaultFillColor,% + required interface distance/.get=\tikzumlRequiredInterfaceDefaultDistance, required interface width/.get=\tikzumlRequiredInterfaceDefaultWidth, required interface padding/.get=\tikzumlRequiredInterfaceDefaultPadding,% + provided interface distance/.get=\tikzumlProvidedInterfaceDefaultDistance, provided interface width/.get=\tikzumlProvidedInterfaceDefaultWidth, provided interface padding/.get=\tikzumlProvidedInterfaceDefaultPadding,% + port width/.get=\tikzumlPortDefaultWidth, fill port/.get=\tikzumlPortDefaultFillColor,% + fill assembly connector/.get=\tikzumlAssemblyConnectorDefaultFillColor% +}% +% +% utility : change default colors +\newcommand{\tikzumlset}[1]{% + \pgfkeys{/tikzuml/.cd,#1}% + \pgfkeys{/tikzuml/.cd, text/.get=\tikzumlDefaultTextColor, draw/.get=\tikzumlDefaultDrawColor, font/.get=\tikzumlDefaultFont,% + x/.get=\tikzumlDefaultX, y/.get=\tikzumlDefaultY,% + package type/.get=\tikzumlPackageDefaultType, fill package/.get=\tikzumlPackageDefaultFillColor,% + class width/.get=\tikzumlClassDefaultWidth, simple interface width/.get=\tikzumlSimpleInterfaceDefaultWidth, class type/.get=\tikzumlClassDefaultType, fill class/.get=\tikzumlClassDefaultFillColor, fill template/.get=\tikzumlClassTemplateFillColorDefaultFillColor,% + narynode width/.get=\tikzumlNaryNodeWidth,% + relation geometry/.get=\tikzumlRelationDefaultGeometry, relation angle1/.get=\tikzumlRelationDefaultAngleO, relation angle2/.get=\tikzumlRelationDefaultAngleT, relation loopsize/.get=\tikzumlRelationDefaultLoopSize, relation weight/.get=\tikzumlRelationDefaultWeight, relation pos1/.get=\tikzumlRelationDefaultPosO, relation pos2/.get=\tikzumlRelationDefaultPosT, relation pos stereo/.get=\tikzumlRelationDefaultPosStereo,% + note width/.get=\tikzumlNoteDefaultWidth, fill note/.get=\tikzumlNoteDefaultFillColor,% + fill system/.get=\tikzumlSystemDefaultFillColor,% + fill usecase/.get=\tikzumlUseCaseDefaultFillColor,% + actor below/.get=\tikzumlActorDefaultBelow,% + state join width/.get=\tikzumlStateJoinDefaultWidth,% + state decision width/.get=\tikzumlStateDecisionDefaultWidth,% + state initial width/.get=\tikzumlStateInitialDefaultWidth,% + state final width/.get=\tikzumlStateFinalDefaultWidth,% + state enter width/.get=\tikzumlStateEnterDefaultWidth,% + state exit width/.get=\tikzumlStateExitDefaultWidth,% + state end width/.get=\tikzumlStateEndDefaultWidth,% + state history width/.get=\tikzumlStateHistoryDefaultWidth,% + state deep history width/.get=\tikzumlStateDeepHistoryDefaultWidth,% + state width/.get=\tikzumlStateDefaultWidth, fill state/.get=\tikzumlStateDefaultFillColor,% + object stereo/.get=\tikzumlObjectDefaultStereo, fill object/.get=\tikzumlObjectDefaultFillColor,% + call dt/.get=\tikzumlCallDefaultDT, call padding/.get=\tikzumlCallDefaultPadding, call type/.get=\tikzumlCallDefaultType, fill call/.get=\tikzumlCallDefaultFillColor,% + fragment type/.get=\tikzumlFragmentDefaultType, fragment inner xsep/.get=\tikzumlFragmentDefaultXSep, fragment inner ysep/.get=\tikzumlFragmentDefaultYSep, fill fragment/.get=\tikzumlFragmentDefaultFillColor,% + create call dt/.get=\tikzumlCreateCallDT,% + component width/.get=\tikzumlComponentDefaultWidth, fill component/.get=\tikzumlComponentDefaultFillColor,% + required interface distance/.get=\tikzumlRequiredInterfaceDefaultDistance, required interface width/.get=\tikzumlRequiredInterfaceDefaultWidth, required interface padding/.get=\tikzumlRequiredInterfaceDefaultPadding,% + provided interface distance/.get=\tikzumlProvidedInterfaceDefaultDistance, provided interface width/.get=\tikzumlProvidedInterfaceDefaultWidth, provided interface padding/.get=\tikzumlProvidedInterfaceDefaultPadding,% + port width/.get=\tikzumlPortDefaultWidth, fill port/.get=\tikzumlPortDefaultFillColor,% + fill assembly connector/.get=\tikzumlAssemblyConnectorDefaultFillColor% + }% +}% +% +% define a point +% arg : node/coordinates of the point +\newcommand{\umlpoint}[1]{% + \begin{pgfonlayer}{connections}% + \node[tikzuml control nodes style] at (#1) {};% + \end{pgfonlayer}% +}% +% +\newcommand{\tikzumlskipescape}[3][_]{% +\begingroup% + \def\_{#1}\edef\x{\endgroup% + \def\noexpand\csname #3\endcsname{#2}}\x% +}% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% class diagrams % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\pgfkeys{/tikzuml/relation/.cd, attr1/.style args={#1|#2}{arg1=#1, mult1=#2},% + attr2/.style args={#1|#2}{arg2=#1, mult2=#2},% + attr/.style args={#1|#2}{arg=#1, mult=#2},% + recursive/.style args={#1|#2|#3}{angle1=#1, angle2=#2, loopsize=#3},% + anchors/.style args={#1 and #2}{anchor1=#1, anchor2=#2},% + recursive direction/.style args={#1 to #2}{recursive direction start=#1, recursive direction end=#2}% +}% +% +\pgfkeys{/tikzuml/note/.cd, anchors/.style args={#1 and #2}{anchor1=#1, anchor2=#2}}% +% +\tikzstyle{tikzuml simpleclass style}=[rectangle, minimum height=2em, node distance=2em]% +\tikzstyle{tikzuml simpleinterface style}=[circle, minimum height=1em, node distance=1em]% +\tikzstyle{tikzuml class style}=[rectangle split, rectangle split parts=3, rectangle split part align={center, left, left}, minimum height=2em, node distance=2em]% +\tikzstyle{tikzuml narynode style}=[diamond]% +\tikzstyle{tikzuml template style}=[dashed, inner ysep=0.5em, inner xsep=1ex]% +\tikzstyle{tikzuml control nodes style}=[fill=black, inner sep=1.5pt, circle]% +% +\tikzstyle{tikzuml association style}=[color=\tikzumlDefaultDrawColor, -]% +\tikzstyle{tikzuml bidirectional association style}=[color=\tikzumlDefaultDrawColor, angle45-angle45]% +\tikzstyle{tikzuml unidirectional association style}=[color=\tikzumlDefaultDrawColor, -angle 45]% +\tikzstyle{tikzuml aggregation style}=[color=\tikzumlDefaultDrawColor, open diamond-]% +\tikzstyle{tikzuml unidirectional aggregation style}=[color=\tikzumlDefaultDrawColor, open diamond-angle 45]% +\tikzstyle{tikzuml composition style}=[color=\tikzumlDefaultDrawColor, diamond-]% +\tikzstyle{tikzuml unidirectional composition style}=[color=\tikzumlDefaultDrawColor, diamond-angle 45]% +\tikzstyle{tikzuml nesting style}=[color=\tikzumlDefaultDrawColor]% +\tikzstyle{tikzuml dependency style}=[color=\tikzumlDefaultDrawColor, -angle 45, dashed]% +\tikzstyle{tikzuml import style}=[color=\tikzumlDefaultDrawColor, -angle 45, dashed]% +\tikzstyle{tikzuml inherit style}=[color=\tikzumlDefaultDrawColor, -open triangle 45]% +\tikzstyle{tikzuml implements style}=[color=\tikzumlDefaultDrawColor, -open triangle 45, dashed]% +% +\pgfkeys{/tikzuml/assemblyconnectorrelation/.cd, anchors/.style args={#1 and #2}{anchor1=#1, anchor2=#2}}% +% +\newcounter{tikzumlPackageClassNum}% +\newcounter{tikzumlPackageSubPackageNum}% +\newcounter{tikzumlRelationNum}% +\setcounter{tikzumlRelationNum}{1}% +\newcounter{tikzumlNoteNum}% +\setcounter{tikzumlNoteNum}{1}% +% +\newcounter{pos}% +\newcounter{posT}% +\newcounter{posStereo}% +% +\newcounter{tikzumlPackageLevel}% +\setcounter{tikzumlPackageLevel}{0}% +% +\newif\iftikzumlpackageSimpleStyle% +\newif\iftikzumlclassSimpleStyle% +\newif\iftikzumlclassCircleShape% +\newif\iftikzumlpackageWithoutCoords% +\newif\iftikzumlclassWithoutCoords% +\newif\iftikzumlassocclassWithoutCoords% +\newif\iftikzumlnoteWithoutCoords% +% +% define a uml package +% arg : package name +% optional : x, y: coordinates of the package +% type: stereotype of the package +% name: name of the package node +% draw, fill, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the package position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newenvironment{umlpackage}[2][]{% + \pgfkeys{/tikzuml/package/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, style/.style={},% + name/.initial=tikzumlEmpty, type/.initial=\tikzumlPackageDefaultType, draw/.initial=\tikzumlDefaultDrawColor,% + fill/.initial=\tikzumlPackageDefaultFillColor, text/.initial=\tikzumlDefaultTextColor,% + no coords/.is if=tikzumlpackageWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/package/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/package/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/package/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlpackage, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/package/.cd, #1}% + \pgfkeys{/tikzuml/package/.cd, x/.get=\tikzumlPackageXShift, y/.get=\tikzumlPackageYShift, name/.get=\tikzumlPackageName, type/.get=\tikzumlPackageTypeTmp,% + draw/.get=\tikzumlPackageDrawColor, fill/.get=\tikzumlPackageFillColor,% + text/.get=\tikzumlPackageTextColor% + }% + % + + % + \ifthenelse{\equal{\tikzumlPackageTypeTmp}{tikzumlEmpty}}{% + \def\tikzumlPackageType{}% + }{% + \expandafter\def\expandafter\tikzumlPackageType\expandafter{$\ll$\tikzumlPackageTypeTmp$\gg$ \\}% + }% + % + \ifnum\thetikzumlPackageLevel>0% + \let\tikzumlPackage@nameold\tikzumlPackage@fitname% + \def\tikzumlPackage@name{#2}% + \begingroup% + \def\_{@}\edef\x{\endgroup% + \def\noexpand\tikzumlPackage@fitname{\tikzumlPackage@name}}\x% + \let\tikzumlPackage@parentold\tikzumlPackage@parent% + \edef\tikzumlPackage@parent{\tikzumlPackage@parentold @@\tikzumlPackage@nameold}% + \else% + \def\tikzumlPackage@parent{}% + \def\tikzumlPackage@name{#2}% + \begingroup% + \def\_{@}\edef\x{\endgroup% + \def\noexpand\tikzumlPackage@fitname{\tikzumlPackage@name}}\x% + \fi% + % + \let\tikzumlPackage@nodeNameold\tikzumlPackage@nodeName% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlPackage@nodeName{\tikzumlPackage@name}}\x% + % + \ifthenelse{\equal{\tikzumlPackageName}{tikzumlEmpty}}{}{% + \def\tikzumlPackage@nodeName{\tikzumlPackageName}% + }% + % + \StrSubstitute{\tikzumlPackage@nodeName}{.}{@POINT@}{\tikzumlPackage@nodeName}% + % + \expandafter\gdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{}% + % + \setcounter{tikzumlPackageClassNum}{0}% + \setcounter{tikzumlPackageSubPackageNum}{0}% + \stepcounter{tikzumlPackageLevel}% + % + \begin{scope}[xshift=\tikzumlPackageXShift cm, yshift=\tikzumlPackageYShift cm]% +}{% + \addtocounter{tikzumlPackageLevel}{-1}% + \begin{pgfonlayer}{package\thetikzumlPackageLevel}% + % + % if contains no class, and not simple, one define a fictive node to enable the fit option + \ifnum\c@tikzumlPackageClassNum=0% + \ifnum\c@tikzumlPackageSubPackageNum=0% + \iftikzumlpackageWithoutCoords% + \node[inner sep=1.5ex, /tikzuml/package/style] (\tikzumlPackage@nodeName-root) {\phantom{\tikzumlPackage@nodeName}};% + \else% + \node[inner sep=1.5ex, /tikzuml/package/style] (\tikzumlPackage@nodeName-root) at (0,0) {\phantom{\tikzumlPackage@nodeName}};% + \fi% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{(\tikzumlPackage@nodeName-root)}% + \fi% + \fi% + % + \ifnum\c@tikzumlPackageLevel>0% + \def\tikzumlPackageFitTmp{\csname tikzumlPackageFit\tikzumlPackage@parent\endcsname}% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent\endcsname{\tikzumlPackageFitTmp (\tikzumlPackage@nodeName) (\tikzumlPackage@nodeName-caption)}% + \stepcounter{tikzumlPackageSubPackageNum}% + \fi% + % + \node[draw=\tikzumlPackageDrawColor, fill=\tikzumlPackageFillColor, text=\tikzumlPackageTextColor, font=\tikzumlDefaultFont, inner sep=1.5ex, /tikzuml/package/style, fit = \csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname] (\tikzumlPackage@nodeName) {};% + \node[draw=\tikzumlPackageDrawColor, fill=\tikzumlPackageFillColor, text=\tikzumlPackageTextColor, font=\tikzumlDefaultFont, minimum height=1.5em, outer ysep=-0.3, anchor=south west] (\tikzumlPackage@nodeName-caption) at (\tikzumlPackage@nodeName.north west) {\begin{tabular}{c} \tikzumlPackageType \textbf{\tikzumlPackage@name}\end{tabular}};% + \end{pgfonlayer}% + \end{scope}% +}% +% +% shortcut to define an empty package +\newcommand{\umlemptypackage}[2][]{\begin{umlpackage}[#1]{#2} \end{umlpackage}}% +% +% define a uml class +% args : name of the class +% attributes of the class +% operations of the class +% optional : x, y: coordinates of the class +% width: of the class node +% type: type of class (class, interface, typedef, enum) +% tags: tagged values of class +% template: template parameters +% simple: if used, class is empty and drawn with a rectangle +% circle: if used with simple, class is empty and drawn with a circle +% draw, fill, fill template, and text: colors +% style: to manage every default TikZ option +% no coords: to tell that the class position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlclass}[4][]{% + \pgfkeys{/tikzuml/class/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlClassDefaultWidth, type/.initial=\tikzumlClassDefaultType,% + tags/.initial={}, style/.style={},% + template/.initial={}, name/.initial=tikzumlEmpty,% + draw/.initial=\tikzumlDefaultDrawColor,% + fill template/.initial=\tikzumlClassTemplateFillColorDefaultFillColor,% + fill/.initial=\tikzumlClassDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + simple/.is if=tikzumlclassSimpleStyle, circle/.is if=tikzumlclassCircleShape, no coords/.is if=tikzumlclassWithoutCoords,% + simple=false, circle=false, no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/class/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/class/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/class/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlclass, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/class/.cd,#1}% + % + \iftikzumlclassSimpleStyle% + \iftikzumlclassCircleShape% + \pgfkeys{/tikzuml/class/.cd, width/.initial=\tikzumlSimpleInterfaceDefaultWidth}% + \fi% + \fi% + % + \pgfkeys{/tikzuml/class/.cd, x/.get=\tikzumlClassX, y/.get=\tikzumlClassY, width/.get=\tikzumlClassMinimumWidth,% + type/.get=\tikzumlClassTypeTmp, tags/.get=\tikzumlClassTagsTmp, template/.get=\tikzumlClassTemplateFillColorParam,% + name/.get=\tikzumlClassName,% + draw/.get=\tikzumlClassDrawColor, fill/.get=\tikzumlClassFillColor,% + text/.get=\tikzumlClassTextColor, fill template/.get=\tikzumlClassTemplateFillColor% + }% + % + \ifthenelse{\equal{\tikzumlClassTypeTmp}{class}\OR\equal{\tikzumlClassTypeTmp}{abstract}}{% + \def\tikzumlClassType{}% + }{% + \expandafter\def\expandafter\tikzumlClassType\expandafter{$\ll$\tikzumlClassTypeTmp$\gg$ \\}% + }% + % + \ifthenelse{\equal{\tikzumlClassTagsTmp}{}}{% + \def\tikzumlClassTags{}% + }{% + \def\tikzumlClassTags{\\ \{\tikzumlClassTagsTmp\}}% + }% + % + \ifthenelse{\equal{\tikzumlClassTemplateFillColorParam}{}}{% + \def\tikzumlClassVPadding{}% + \def\tikzumlClassHPadding{}% + }{% + \def\tikzumlClassVPadding{\vspace{0.1em} \\}% + \def\tikzumlClassHPadding{\hspace{0.5ex} $ $}% + }% + % + \def\tikzumlClassName{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlClassNodeName{\tikzumlClassName}}\x% + % + \ifthenelse{\equal{\tikzumlClassName}{tikzumlEmpty}}{}{% + \def\tikzumlClassNodeName{\tikzumlClassName}% + }% + % + \StrSubstitute{\tikzumlClassNodeName}{:}{@COLON@}[\tikzumlClassNodeName]% + \StrSubstitute{\tikzumlClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlClassNodeName]% + % + \ifthenelse{\equal{\tikzumlClassTypeTmp}{abstract}}{% + \let\tikzumlClassNameOld\tikzumlClassName% + \def\tikzumlClassName{{\it \tikzumlClassNameOld}}% + }{}% + % + \def\tikzumlClassPos{\tikzumlClassX,\tikzumlClassY}% + \def\tikzumlClassAttributes{#3}% + \def\tikzumlClassOperations{#4}% + % + \iftikzumlclassSimpleStyle% + \iftikzumlclassWithoutCoords% + \iftikzumlclassCircleShape% + \node[tikzuml simpleinterface style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) {};% + \node[anchor=south] (\tikzumlClassNodeName-label) at (\tikzumlClassNodeName.north) {\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + };% + \else% + \node[tikzuml simpleclass style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) {\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + };% + \fi% + \else% + \iftikzumlclassCircleShape% + \node[tikzuml simpleinterface style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) at (\tikzumlClassPos) {}; + \node[anchor=south] (\tikzumlClassNodeName-label) at (\tikzumlClassNodeName.north){\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + };% + \else% + \node[tikzuml simpleclass style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) at (\tikzumlClassPos) {\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + };% + \fi% + \fi% + \else% + \iftikzumlclassWithoutCoords% + \node[tikzuml class style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) {\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + \nodepart{second}% + \begin{tabular}{l}% + \tikzumlClassAttributes% + \end{tabular}% + \nodepart{third}% + \begin{tabular}{l}% + \tikzumlClassOperations% + \end{tabular}% + };% + \else% + \node[tikzuml class style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) at (\tikzumlClassPos) {\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + \nodepart{second}% + \begin{tabular}{l}% + \tikzumlClassAttributes% + \end{tabular}% + \nodepart{third}% + \begin{tabular}{l}% + \tikzumlClassOperations% + \end{tabular}% + };% + \fi% + \fi% + % + \ifthenelse{\equal{\tikzumlClassTemplateFillColorParam}{}}{}{% + \draw (\tikzumlClassNodeName.north east) node[tikzuml template style, name=\tikzumlClassNodeName-template, draw=\tikzumlClassDrawColor, fill=\tikzumlClassTemplateFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont] {\tikzumlClassTemplateFillColorParam};% + }% + % + % add to fit + \ifnum\c@tikzumlPackageLevel>0% + \edef\tikzumlPackageFitOld{\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname}% + \ifthenelse{\equal{\tikzumlClassTemplateFillColorParam}{}}{% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlClassNodeName)}% + }{% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlClassNodeName) (\tikzumlClassNodeName-template)}% + }% + \stepcounter{tikzumlPackageClassNum}% + \fi% + \ifnum\c@tikzumlComponentLevel>0% + \def\tikzumlComponentFitTmp{\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname}% + \ifthenelse{\equal{\tikzumlClassTemplateFillColorParam}{}}{% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{\tikzumlComponentFitTmp (\tikzumlClassNodeName)}% + }{% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{\tikzumlComponentFitTmp (\tikzumlClassNodeName) (\tikzumlClassNodeName-template)}% + }% + \stepcounter{tikzumlComponentSubComponentNum}% + \fi% +}% +% +% shortcuts for interface, enum and typedef environments +\newcommand{\umlabstract}[4][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{type}}{% + \errmessage{TIKZUML ERROR : in umlabstract, forbidden option type}% + }{}% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlclass[type=abstract,#1]{#2}{#3}{#4}% +}% +\newcommand{\umlinterface}[4][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{type}}{% + \errmessage{TIKZUML ERROR : in umlinterface, forbidden option type}% + }{}% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlclass[type=interface,#1]{#2}{#3}{#4}% +}% +\newcommand{\umltypedef}[4][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{type}}{% + \errmessage{TIKZUML ERROR : in umltypedef, forbidden option type}% + }{}% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlclass[type=typedef,#1]{#2}{#3}{#4}% +}% +\newcommand{\umlenum}[4][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{type}}{% + \errmessage{TIKZUML ERROR : in umlenum, forbidden option type}% + }{}% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlclass[type=enum,#1]{#2}{#3}{#4} +}% +% +% shortcut to define an empty class +\newcommand{\umlemptyclass}[2][]{\umlclass[#1]{#2}{}{}}% +\newcommand{\umlsimpleclass}[2][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{simple}}{% + \errmessage{TIKZUML ERROR : in umlsimpleclass, forbidden option simple}% + }{}% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlemptyclass[simple, #1]{#2}% +}% +% +\newcommand{\umlsimpleinterface}[2][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{simple}}{% + \errmessage{TIKZUML ERROR : in umlsimpleinterface, forbidden option simple}% + }{% + \ifthenelse{\equal{\keyname}{circle}}{% + \errmessage{TIKZUML ERROR : in umlsimpleinterface, forbidden option circle}% + }{}% + }% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlsimpleclass[circle, #1]{#2}% +}% +% underline the text for static arg +\newcommand{\umlstatic}[1]{\underline{#1}}% +\newcommand{\umlvirt}[1]{\textit{#1}}% +% +% define node for n-ary association +\newcommand{\umlNarynode}[2][]{% + \def\tikzumlNaryNodeAnchor{.north} + \def\tikzumlNaryNodeLabelPos{above} + \pgfkeys{/tikzuml/narynode/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlNaryNodeDefaultWidth, name/.initial=tikzumlEmpty,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlClassDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor, style/.style={},% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}}{% + \def\tikzumlNaryNodeAnchor{.north}% + \def\tikzumlNaryNodeLabelPos{above}% + }{% + \ifthenelse{\equal{\keyname}{above left}}{% + \def\tikzumlNaryNodeAnchor{.north west}% + \def\tikzumlNaryNodeLabelPos{above left}% + }{% + \ifthenelse{\equal{\keyname}{left}}{% + \def\tikzumlNaryNodeAnchor{.west}% + \def\tikzumlNaryNodeLabelPos{left}% + }{% + \ifthenelse{\equal{\keyname}{below left}}{% + \def\tikzumlNaryNodeAnchor{.south west}% + \def\tikzumlNaryNodeLabelPos{below left}% + }{% + \ifthenelse{\equal{\keyname}{below}}{% + \def\tikzumlNaryNodeAnchor{.south}% + \def\tikzumlNaryNodeLabelPos{below}% + }{% + \ifthenelse{\equal{\keyname}{below right}}{% + \def\tikzumlNaryNodeAnchor{.south east}% + \def\tikzumlNaryNodeLabelPos{below right}% + }{% + \ifthenelse{\equal{\keyname}{right}}{% + \def\tikzumlNaryNodeAnchor{.east}% + \def\tikzumlNaryNodeLabelPos{right}% + }{% + \ifthenelse{\equal{\keyname}{above right}}{% + \def\tikzumlNaryNodeAnchor{.north east}% + \def\tikzumlNaryNodeLabelPos{above right}% + }{% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/narynode/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/narynode/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlNarynode, invalid option \keyname}% + }% + }% + }% + }% + }% + }% + }% + }% + }% + }% + \pgfkeys{/tikzuml/narynode/.cd,#1}% + \pgfkeys{/tikzuml/narynode/.cd, x/.get=\tikzumlNaryNodeX, y/.get=\tikzumlNaryNodeY, width/.get=\tikzumlNaryNodeMinimumWidth,% + name/.get=\tikzumlNaryNodeName,% + draw/.get=\tikzumlNaryNodeDrawColor, fill/.get=\tikzumlNaryNodeFillColor,% + text/.get=\tikzumlNaryNodeTextColor% + }% + % + \def\tikzumlNaryName{#2}% + % + \ifthenelse{\equal{\tikzumlNaryNodeName}{tikzumlEmpty}}{% + \edef\tikzumlNaryNodeName{\tikzumlNaryName}% + }{% + \edef\tikzumlNaryNodeName{\tikzumlNaryNodeName}% + }% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlNaryNode@nodeName{\tikzumlNaryNodeName}}\x% + % + \StrSubstitute{\tikzumlNaryNode@nodeName}{:}{@COLON@}[\tikzumlNaryNode@nodeName]% + \StrSubstitute{\tikzumlNaryNode@nodeName}{\_}{@UNDERSCORE@}[\tikzumlNaryNode@nodeName]% + % + \def\tikzumlNarynodePos{\tikzumlNaryNodeX,\tikzumlNaryNodeY}% + % + \node[tikzuml narynode style, draw=\tikzumlNaryNodeDrawColor, fill=\tikzumlNaryNodeFillColor, text=\tikzumlNaryNodeTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlNaryNodeMinimumWidth, minimum height=\tikzumlNaryNodeMinimumWidth, /tikzuml/narynode/style] (\tikzumlNaryNode@nodeName) at (\tikzumlNarynodePos) {};% + \draw (\tikzumlNaryNode@nodeName\tikzumlNaryNodeAnchor) node[\tikzumlNaryNodeLabelPos] {\tikzumlNaryName};% + % + % add to fit + \ifnum\c@tikzumlPackageLevel>0% + \edef\tikzumlPackageFitOld{\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname}% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlNaryNode@nodeName)}% + \stepcounter{tikzumlPackageClassNum}% + \fi% +}% +% +\newdimen\tikzumlNestingSymbolSize% +% +% main command to define a relation between two classes +% args : src class +% dest class +% optional : geometry: geometry of the line +% weight: barycentric weight of the middle part when geometry is a 3-line +% arm1, arm2: lengths of first or last part when geometry id a 3-line +% arg1, arg2, arg: name of the src/dest/dest side class type attribute defined by the relation +% mult1, mult2, mult: multiplicity of the src/dest/dest side class type attribute defined by the relation +% pos1, pos2, pos: position of the src/dest/dest side class type attribute defined by the relation +% align1, align2, align: text justification of the src/dest/dest side class type attribute defined by the relation +% anchor1, anchor2: src/dest anchors on linked classes +% angle1, angle2, loopsize: start angle, end angle and size of the relation (only if recursive) +% stereo: stereotype of the relation +% pos stereo: position of the stereotype on the relation +% style: style of the relation (association, aggregation, composition, inherit, ...) +% name: rootname used for naming nodes of the relation +% recursive mode: type of recursive arrow (transition for state diagrams, or default) +% recursive direction start/end: when transition relation, start/end directions of the relation arrow +\newcommand{\umlrelation}[3][]{% + \pgfkeys{/tikzuml/relation/.cd, geometry/.initial=\tikzumlRelationDefaultGeometry, weight/.initial=\tikzumlRelationDefaultWeight,% + arm1/.initial=auto, arm2/.initial=auto,% + arg1/.initial={}, arg2/.initial={}, arg/.initial={},% + mult1/.initial={}, mult2/.initial={}, mult/.initial={},% + pos1/.initial=\tikzumlRelationDefaultPosO, pos2/.initial=\tikzumlRelationDefaultPosT, pos/.initial=tikzumlEmpty,% + align1/.initial={}, align2/.initial={}, align/.initial={},% + anchor1/.initial=tikzumlEmpty, anchor2/.initial=tikzumlEmpty,% + angle1/.initial=\tikzumlRelationDefaultAngleO, angle2/.initial=\tikzumlRelationDefaultAngleT, loopsize/.initial=\tikzumlRelationDefaultLoopSize,% + stereo/.initial={}, pos stereo/.initial=\tikzumlRelationDefaultPosStereo,% + style/.initial=->, style2/.style={}, name/.initial=relation-\thetikzumlRelationNum,% + recursive mode/.initial=default, recursive direction start/.initial=right,% + recursive direction end/.initial=bottom,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{with port}% + \OR\equal{\keyname}{interface}% + \OR\equal{\keyname}{padding}% + \OR\equal{\keyname}{width}% + \OR\equal{\keyname}{first arm}% + \OR\equal{\keyname}{second arm}% + \OR\equal{\keyname}{middle arm}% + \OR\equal{\keyname}{last arm}% + \OR\equal{\keyname}{distance}}{}{% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/relation/.cd, style2/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/relation/.cd, style2/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlrelation, invalid option \keyname}% + }% + }% + }% + \pgfkeys{/tikzuml/relation/.cd,#1}% + \pgfkeys{/tikzuml/relation/.cd, geometry/.get=\tikzumlRelationGeometry, weight/.get=\tikzumlRelationWeight,% + arm1/.get=\tikzumlRelationArmO, arm2/.get=\tikzumlRelationArmT,% + arg1/.get=\tikzumlRelationAttrName, arg2/.get=\tikzumlRelationAttrNameTO, arg/.get=\tikzumlRelationAttrNameTT,% + mult1/.get=\tikzumlRelationMultiplicity, mult2/.get=\tikzumlRelationMultiplicityTO, mult/.get=\tikzumlRelationMultiplicityTT,% + pos1/.get=\tikzumlRelationPosition, pos2/.get=\tikzumlRelationPositionTO, pos/.get=\tikzumlRelationPositionTT,% + align1/.get=\tikzumlRelationAlign, align2/.get=\tikzumlRelationAlignTO, align/.get=\tikzumlRelationAlignTT,% + anchor1/.get=\tikzumlRelationSrcAnchor, anchor2/.get=\tikzumlRelationDestAnchor,% + angle1/.get=\tikzumlRelationStartAngle, angle2/.get=\tikzumlRelationEndAngle, loopsize/.get=\tikzumlRelationLoopSize,% + stereo/.get=\tikzumlRelationStereoType, pos stereo/.get=\tikzumlRelationPositionStereotype,% + style/.get=\tikzumlRelationStyle, name/.get=\tikzumlRelationName,% + recursive mode/.get=\tikzumlRelationRecursiveMode,% + recursive direction start/.get=\tikzumlRelationRecursiveDirectionStart,% + recursive direction end/.get=\tikzumlRelationRecursiveDirectionEnd% + }% + % + \def\tikzumlSrcClassName{#2}% + % + % managing \_ in class names for node names + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlSrcClassNodeName{\tikzumlSrcClassName}}\x% + % + \StrSubstitute{\tikzumlSrcClassNodeName}{:}{@COLON@}[\tikzumlSrcClassNodeName]% + \StrSubstitute{\tikzumlSrcClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlSrcClassNodeName]% + % + \def\tikzumlDestClassName{#3}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlDestClassNodeName{\tikzumlDestClassName}}\x% + % + \StrSubstitute{\tikzumlDestClassNodeName}{:}{@COLON@}[\tikzumlDestClassNodeName]% + \StrSubstitute{\tikzumlDestClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlDestClassNodeName]% + % + % managing alias keys + \def\tikzumlRelationAttrNameT{\tikzumlRelationAttrNameTO\tikzumlRelationAttrNameTT}% + \def\tikzumlRelationMultiplicityT{\tikzumlRelationMultiplicityTO\tikzumlRelationMultiplicityTT}% + \def\tikzumlRelationAlignT{\tikzumlRelationAlignTO\tikzumlRelationAlignTT}% + \def\posAttrName{}% + \def\posMultiplicity{}% + \def\posAttrNameT{}% + \def\posMultiplicityT{}% + % + \ifthenelse{\equal{\tikzumlRelationPositionTT}{tikzumlEmpty}}{% + \def\tikzumlRelationPositionT{\tikzumlRelationPositionTO}% + }{% + \def\tikzumlRelationPositionT{\tikzumlRelationPositionTT}% + }% + % + \def\attrAlign{}% + \def\multAlign{}% + \def\attrAlignT{}% + \def\multAlignT{}% + % + \ifthenelse{\equal{\tikzumlRelationAlign}{left}}{% + \def\attrAlign{above right}% + \def\multAlign{below right}% + }{% + \ifthenelse{\equal{\tikzumlRelationAlign}{right}}{% + \def\attrAlign{above left}% + \def\multAlign{below left}% + }{}% + }% + % + \ifthenelse{\equal{\tikzumlRelationAlignT}{left}}{% + \def\attrAlignT{above right}% + \def\multAlignT{below right}% + }{% + \ifthenelse{\equal{\tikzumlRelationAlignT}{right}}{% + \def\attrAlignT{above left}% + \def\multAlignT{below left}% + }{}% + }% + % + % def stereotype + \ifthenelse{\equal{\tikzumlRelationStereoType}{}}{% + \def\stereotype{}% + }{% + \def\stereotype{$\ll$\tikzumlRelationStereoType$\gg$}% + }% + + % def anchors macros + \ifthenelse{\equal{\tikzumlRelationSrcAnchor}{tikzumlEmpty}}{% + \def\tikzumlRelationSrcAnchor{}% + }{% + \let\tikzumlRelationSrcAnchorold\tikzumlRelationSrcAnchor% + \def\tikzumlRelationSrcAnchor{.\tikzumlRelationSrcAnchorold}% + }% + % + \ifthenelse{\equal{\tikzumlRelationDestAnchor}{tikzumlEmpty}}{% + \def\tikzumlRelationDestAnchor{}% + }{% + \let\tikzumlRelationDestAnchorold\tikzumlRelationDestAnchor% + \def\tikzumlRelationDestAnchor{.\tikzumlRelationDestAnchorold}% + }% + % + \setcounter{pos}{100*\real{\tikzumlRelationPosition}}% + \setcounter{posT}{100*\real{\tikzumlRelationPositionT}}% + \setcounter{posStereo}{100*\real{\tikzumlRelationPositionStereotype}}% + % + \pgfmathsetmacro{\tikzumlRelationWeightT}{1.0-\tikzumlRelationWeight}% + % + %\newcounter{tikzumlControlNodesNum}% + %\setcounter{tikzumlControlNodesNum}{0}% + % + \node[inner sep=0] (\tikzumlRelationName-middle) at (barycentric cs:\tikzumlSrcClassNodeName=\tikzumlRelationWeightT,\tikzumlDestClassNodeName=\tikzumlRelationWeight) {};% + % + % straight line + \ifthenelse{\equal{\tikzumlRelationGeometry}{--}}% + {% + \ifthenelse{\equal{\tikzumlSrcClassNodeName}{\tikzumlDestClassNodeName}}{% + \def\arcNum{1}% + \def\arcNumT{1}% + % + \ifthenelse{\equal{\tikzumlRelationRecursiveMode}{default}}{% + \xdef\tikzumlLastArc{node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName) edge[in=\tikzumlRelationEndAngle, out=\tikzumlRelationStartAngle, distance=\tikzumlRelationLoopSize] \tikzumlLastArc% + node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center] {} (\tikzumlDestClassNodeName) }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveMode}{transition}}{% + \xdef\tikzumlFirstArc{node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center] {}}% + \xdef\tikzumlMidOneArc{node[midway, inner sep=0, name=\tikzumlRelationName-3, anchor=center] {}}% + % + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{\tikzumlRelationRecursiveDirectionEnd}}{% + \def\numArcs{3}% + \xdef\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-5, anchor=center] {}}% + % + \begin{pgfonlayer}{connections}% + \draw (\tikzumlSrcClassNodeName) edge[in=\tikzumlRelationEndAngle, out=\tikzumlRelationStartAngle, distance=\tikzumlRelationLoopSize, draw=none] % + node[midway, inner sep=0, name=\tikzumlRelationName-tmp, anchor=center] {} (\tikzumlDestClassNodeName);% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}\OR\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \node[inner sep=0, name=\tikzumlRelationName-2] at (\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle -| \tikzumlRelationName-tmp) {};% + \node[inner sep=0, name=\tikzumlRelationName-4] at (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle -| \tikzumlRelationName-tmp) {};% + }{% + \node[inner sep=0, name=\tikzumlRelationName-2] at (\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle |- \tikzumlRelationName-tmp) {};% + \node[inner sep=0, name=\tikzumlRelationName-4] at (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle |- \tikzumlRelationName-tmp) {};% + }% + \end{pgfonlayer}% + }{% + \def\numArcs{4}% + \xdef\tikzumlMidTwoArc{node[midway, inner sep=0, name=\tikzumlRelationName-5, anchor=center] {}}% + \xdef\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-7, anchor=center] {}}% + % + \begin{pgfonlayer}{connections}% + \draw (\tikzumlSrcClassNodeName) edge[in=\tikzumlRelationEndAngle, out=\tikzumlRelationStartAngle, distance=\tikzumlRelationLoopSize, draw=none] % + node[midway, name=\tikzumlRelationName-4, anchor=center] {} (\tikzumlDestClassNodeName);% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}\OR\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \node[inner sep=0, name=\tikzumlRelationName-2] at (\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle -| \tikzumlRelationName-4) {};% + \node[inner sep=0, name=\tikzumlRelationName-6] at (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle |- \tikzumlRelationName-4) {};% + }{% + \node[inner sep=0, name=\tikzumlRelationName-2] at (\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle |- \tikzumlRelationName-4) {};% + \node[inner sep=0, name=\tikzumlRelationName-6] at (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle -| \tikzumlRelationName-4) {};% + }% + \end{pgfonlayer}% + }% + % + \ifnum\numArcs=4% + \ifnum\theposStereo>300% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-300)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype}}% + \else% + \ifnum\theposStereo<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype}}% + \else% + \ifnum\theposStereo>200% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-200)/100}% + \xdef\tikzumlMidTwoArc{\tikzumlMidTwoArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype}}% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype}}% + \fi% + \fi% + \fi% + % + \ifthenelse{\thepos=300\OR\thepos=100}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }% + }% + }% + }{}% + % + \ifthenelse{\thepos=200}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }% + }% + }% + }{}% + % + \ifnum\thepos>300% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-300)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \else% + \ifnum\thepos<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \else% + \ifnum\thepos>200% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-200)/100}% + \xdef\tikzumlMidTwoArc{\tikzumlMidTwoArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \else% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \fi% + \fi% + \fi% + % + \ifthenelse{\theposT=300\OR\theposT=100}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }% + }% + }% + }{}% + \ifthenelse{\theposT=200}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrNameT{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }% + }% + }% + }{}% + % + \ifnum\theposT>300% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-300)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \else% + \ifnum\theposT<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \else% + \ifnum\theposT>200% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-200)/100}% + \xdef\tikzumlMidTwoArc{\tikzumlMidTwoArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \fi% + \fi% + \fi% + \else% + \ifnum\theposStereo>200% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-200)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \ifnum\theposStereo<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + \fi% + % + \ifthenelse{\thepos=100}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }% + }% + }% + }{}% + % + \ifthenelse{\thepos=200}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }% + }% + }% + }{}% + % + \ifnum\thepos>200% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-200)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \else% + \ifnum\thepos<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \else% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \fi% + \fi% + % + \ifthenelse{\theposT=100}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }% + }% + }% + }{}% + % + \ifthenelse{\theposT=200}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }% + }% + }% + }{}% + % + \ifnum\theposT>200% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-200)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \else% + \ifnum\theposT<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \fi% + \fi% + \fi% + % + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{\tikzumlRelationRecursiveDirectionEnd}}{% + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle) -- \tikzumlFirstArc (\tikzumlRelationName-2.center) -- \tikzumlMidOneArc (\tikzumlRelationName-4.center) -- \tikzumlLastArc (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) (\tikzumlRelationName-4) (\tikzumlRelationName-5)}% + \fi% + }{% + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle) -- \tikzumlFirstArc (\tikzumlRelationName-2.center) -- \tikzumlMidOneArc (\tikzumlRelationName-4.center) -- \tikzumlMidTwoArc (\tikzumlRelationName-6.center) -- \tikzumlLastArc (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) (\tikzumlRelationName-4) (\tikzumlRelationName-5) (\tikzumlRelationName-6) (\tikzumlRelationName-7)}% + \fi% + }% + }{}% + }% + }{% + \def\arcNum{1}% + \def\arcNumT{1}% + % + \node[inner sep=0] (\tikzumlRelationName-1) at (\tikzumlRelationName-middle) {};% + \xdef\tikzumlLastArc{node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) }% + \fi% + }% + }{% + % first vertical then horizontal line + \ifthenelse{\equal{\tikzumlRelationGeometry}{|-}}% + {% + %\setcounter{tikzumlControlNodesNum}{1}% + % + \def\tikzumlFirstArc{node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center] {} }% + \def\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-3, anchor=center]{} }% + % + \begin{pgfonlayer}{connections}% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor |- \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + \end{pgfonlayer}% + % + \ifnum\theposStereo>100% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + % + \ifnum\thepos>100% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \def\arcNum{2}% + \else% + \def\arcNum{1}% + \ifnum\thepos=100% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + \fi% + \fi% + % + \ifnum\arcNum=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + % + \ifnum\theposT>100% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \def\arcNumT{2}% + \else% + \def\arcNumT{1}% + \ifnum\theposT=100% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + \fi% + \fi% + % + \ifnum\arcNumT=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + % + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) -- \tikzumlFirstArc (\tikzumlRelationName-2.base) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) }% + \fi% + }{% + % first horizontal then vertical line + \ifthenelse{\equal{\tikzumlRelationGeometry}{-|}}% + {% + %\setcounter{tikzumlControlNodesNum}{1}% + % + \def\tikzumlFirstArc{node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center]{} }% + \def\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-3, anchor=center] {} }% + % + \begin{pgfonlayer}{connections}% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor -| \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + \end{pgfonlayer}% + % + \ifnum\theposStereo>100% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + % + \ifnum\thepos>100% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \def\arcNum{2}% + \else% + \def\arcNum{1}% + \ifnum\thepos=100% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + \fi% + \fi% + % + \ifnum\arcNum=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + % + \ifnum\theposT>100% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \def\arcNumT{2}% + \else% + \def\arcNumT{1}% + \ifnum\theposT=100% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + \fi% + \fi% + % + \ifnum\arcNumT=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + % + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) -- \tikzumlFirstArc (\tikzumlRelationName-2.base) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) }% + \fi% + }{% + % first vertical, then horizontal, finally vertical line + \ifthenelse{\equal{\tikzumlRelationGeometry}{|-|}}% + {% + %\setcounter{tikzumlControlNodesNum}{2}% + % + \def\tikzumlFirstArc{node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center] {} }% + \def\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-5, anchor=center] {} }% + \def\tikzumlMidOneArc{ }% + % + \begin{pgfonlayer}{connections}% + % + \ifthenelse{\equal{\tikzumlRelationArmO}{auto}}{% + \ifthenelse{\equal{\tikzumlRelationArmT}{auto}}{% + \node[inner sep=0] (\tikzumlRelationName-3) at (\tikzumlRelationName-middle) {};% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor |- \tikzumlRelationName-3) {};% + \node[inner sep=0] (\tikzumlRelationName-4) at (\tikzumlRelationName-3 -| \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + }{% + \draw (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor)+(0,\tikzumlRelationArmT) node[inner sep=0, name=\tikzumlRelationName-4] {};% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlRelationName-4 -| \tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) {};% + \node[inner sep=0] (\tikzumlRelationName-3) at (barycentric cs:\tikzumlRelationName-2=0.5,\tikzumlRelationName-4=0.5) {};% + }% + }{% + \draw (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor)+(0,\tikzumlRelationArmO) node[inner sep=0, name=\tikzumlRelationName-2] {};% + \node[inner sep=0] (\tikzumlRelationName-4) at (\tikzumlRelationName-2 -| \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + \node[inner sep=0] (\tikzumlRelationName-3) at (barycentric cs:\tikzumlRelationName-2=0.5,\tikzumlRelationName-4=0.5) {};% + }% + \end{pgfonlayer}% + % + \ifnum\theposStereo>200% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-200)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \ifnum\theposStereo<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + \fi% + % + \ifthenelse{\thepos=200\OR\thepos=100}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{}% + % + \ifthenelse{\thepos>200}{% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-200)/100}% + \def\arcNum{3}% + }{% + \ifthenelse{\thepos<100}{% + \def\arcNum{1}% + }{% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \def\arcNum{2}% + }% + }% + % + \ifnum\arcNum=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=2% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=3% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + % + \ifthenelse{\theposT=200\OR\theposT=100}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{}% + % + \ifthenelse{\theposT>200}{% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-200)/100}% + \def\arcNumT{3}% + }{% + \ifthenelse{\theposT<100}{% + \def\arcNumT{1}% + }{% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \def\arcNumT{2}% + }% + }% + % + \ifnum\arcNumT=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=2% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=3% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + % + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) -- \tikzumlFirstArc (\tikzumlRelationName-2.base) -- \tikzumlMidOneArc (\tikzumlRelationName-4.base) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) (\tikzumlRelationName-4) (\tikzumlRelationName-5) }% + \fi% + }{% + % first horizontal, then vertical, finally horizontal line + \ifthenelse{\equal{\tikzumlRelationGeometry}{-|-}}% + {% + %\setcounter{tikzumlControlNodesNum}{2}% + % + \def\tikzumlFirstArc{node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center] {} }% + \def\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-5, anchor=center] {} }% + \def\tikzumlMidOneArc{}% + % + \begin{pgfonlayer}{connections}% + % + \ifthenelse{\equal{\tikzumlRelationArmO}{auto}}{% + \ifthenelse{\equal{\tikzumlRelationArmT}{auto}}{% + \node[inner sep=0] (\tikzumlRelationName-3) at (\tikzumlRelationName-middle) {};% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor -| \tikzumlRelationName-3) {};% + \node[inner sep=0] (\tikzumlRelationName-4) at (\tikzumlRelationName-3 |- \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + }{% + \draw (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor)+(\tikzumlRelationArmT,0) node[inner sep=0, name=\tikzumlRelationName-4] {};% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlRelationName-4 |- \tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) {};% + \node[inner sep=0] (\tikzumlRelationName-3) at (barycentric cs:\tikzumlRelationName-2=0.5,\tikzumlRelationName-4=0.5) {};% + }% + }{% + \draw (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor)+(\tikzumlRelationArmO,0) node[inner sep=0, name=\tikzumlRelationName-2] {};% + \node[inner sep=0] (\tikzumlRelationName-4) at (\tikzumlRelationName-2 |- \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + \node[inner sep=0] (\tikzumlRelationName-3) at (barycentric cs:\tikzumlRelationName-2=0.5,\tikzumlRelationName-4=0.5) {};% + }% + \end{pgfonlayer}% + % + \ifnum\theposStereo>200% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-200)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \ifnum\theposStereo<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + \fi% + % + \ifthenelse{\thepos=200\OR\thepos=100}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{}% + % + \ifthenelse{\thepos>200}{% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-200)/100}% + \def\arcNum{3}% + }{% + \ifthenelse{\thepos<100}{% + \def\arcNum{1}% + }{% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \def\arcNum{2}% + }% + }% + % + \ifnum\arcNum=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=2% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=3% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + % + \ifthenelse{\theposT=200\OR\theposT=100}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{}% + % + \ifthenelse{\theposT>200}{% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-200)/100}% + \def\arcNumT{3}% + }{% + \ifthenelse{\theposT<100}{% + \def\arcNumT{1}% + }{% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \def\arcNumT{2}% + }% + }% + % + \ifnum\arcNumT=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=2% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=3% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + % + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) -- \tikzumlFirstArc (\tikzumlRelationName-2.base) -- \tikzumlMidOneArc (\tikzumlRelationName-4.base) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) (\tikzumlRelationName-4) (\tikzumlRelationName-5) }% + \fi% + }{% + \errmessage{TIKZUML ERROR : Unknown geometry value !!! It should be in the following list : --, |-, -|, |-|, -|-}% + }% + }% + }% + }% + }% + % + \begin{pgfonlayer}{connections}% + \ifthenelse{\equal{\tikzumlRelationStyle}{tikzuml nesting style}}{% + \pgfarrowsdeclare{nested}{nested}{...} + { + \tikzumlNestingSymbolSize=0.2pt% + \advance\tikzumlNestingSymbolSize by .5\pgflinewidth% + \pgfsetdash{}{0pt} % do not dash + \pgfsetroundjoin % fix join + \pgfsetroundcap % fix cap + \pgfpathmoveto{\pgfpoint{-16*\tikzumlNestingSymbolSize}{0pt}}% + \pgfpatharc{180}{90}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{90}{0}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{0}{-90}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{-90}{-180}{8*\tikzumlNestingSymbolSize}% + \pgfpathmoveto{\pgfpoint{-8*\tikzumlNestingSymbolSize}{8*\tikzumlNestingSymbolSize}}% + \pgfpathlineto{\pgfpoint{-8*\tikzumlNestingSymbolSize}{-8*\tikzumlNestingSymbolSize}}% + \pgfusepathqstroke% + }% + \draw[auto, nested-, font=\tikzumlDefaultFont, \tikzumlRelationStyle, /tikzuml/relation/style2] \tikzumlPath ;% + }{ + \draw[auto, font=\tikzumlDefaultFont, \tikzumlRelationStyle, /tikzuml/relation/style2] \tikzumlPath ;% + } + \end{pgfonlayer}% + % + \stepcounter{tikzumlRelationNum}% +}% +% +% shortcuts of \umlrelation +\newcommand{\umlHVrelation}[3][]{% + \pgfkeys{/tikzuml/HVrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVrelation, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/HVrelation/.cd, #1}% + \umlrelation[geometry=-|, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHrelation}[3][]{% + \pgfkeys{/tikzuml/VHrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHrelation, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/VHrelation/.cd, #1}% + \umlrelation[geometry=|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVHrelation}[3][]{% + \pgfkeys{/tikzuml/HVHrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHrelation, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/HVHrelation/.cd, #1}% + \umlrelation[geometry=-|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHVrelation}[3][]{% + \pgfkeys{/tikzuml/VHVrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVrelation, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/VHVrelation/.cd, #1}% + \umlrelation[geometry=|-|, #1]{#2}{#3}% +}% +% +% +% shortcuts for relations +\newcommand{\umlinherit}[3][]{\umlrelation[style={tikzuml inherit style}, #1]{#2}{#3}}% +\newcommand{\umlimpl}[3][]{\umlrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlreal}[3][]{\umlrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlassoc}[3][]{\umlrelation[style={tikzuml association style}, #1]{#2}{#3}}% +\newcommand{\umlbiassoc}[3][]{\umlrelation[style={tikzuml bidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umluniassoc}[3][]{\umlrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umlaggreg}[3][]{\umlrelation[style={tikzuml aggregation style}, #1]{#2}{#3}}% +\newcommand{\umluniaggreg}[3][]{\umlrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlcompo}[3][]{\umlrelation[style={tikzuml composition style}, #1]{#2}{#3}}% +\newcommand{\umlunicompo}[3][]{\umlrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}}% +\newcommand{\umlimport}[3][]{\umlrelation[style={tikzuml import style}, #1]{#2}{#3}}% +\newcommand{\umlnest}[3][]{\umlrelation[style={tikzuml nesting style}, #1]{#2}{#3}}% +\newcommand{\umldep}[3][]{\umlrelation[style={tikzuml dependency style}, #1]{#2}{#3}}% +\newcommand{\umlfriend}[3][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlfriend, forbidden option stereo}% + }{}% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlrelation[stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVinherit}[3][]{\umlHVrelation[style={tikzuml inherit style}, #1]{#2}{#3}}% +\newcommand{\umlHVimpl}[3][]{\umlHVrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlHVreal}[3][]{\umlHVrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlHVassoc}[3][]{\umlHVrelation[style={tikzuml association style}, #1]{#2}{#3}}% +\newcommand{\umlHVuniassoc}[3][]{\umlHVrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umlHVaggreg}[3][]{\umlHVrelation[style={tikzuml aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlHVuniaggreg}[3][]{\umlHVrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlHVcompo}[3][]{\umlHVrelation[style={tikzuml composition style}, #1]{#2}{#3}}% +\newcommand{\umlHVunicompo}[3][]{\umlHVrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}}% +\newcommand{\umlHVimport}[3][]{\umlHVrelation[style={tikzuml import style}, #1]{#2}{#3}}% +\newcommand{\umlHVnest}[3][]{\umlHVrelation[style={tikzuml nesting style}, #1]{#2}{#3}}% +\newcommand{\umlHVdep}[3][]{\umlHVrelation[style={tikzuml dependency style}, #1]{#2}{#3}}% +\newcommand{\umlHVfriend}[3][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVfriend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVfriend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlrelation[geometry=-|, stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHinherit}[3][]{\umlVHrelation[style={tikzuml inherit style}, #1]{#2}{#3}}% +\newcommand{\umlVHimpl}[3][]{\umlVHrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlVHreal}[3][]{\umlVHrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlVHassoc}[3][]{\umlVHrelation[style={tikzuml association style}, #1]{#2}{#3}}% +\newcommand{\umlVHuniassoc}[3][]{\umlVHrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umlVHaggreg}[3][]{\umlVHrelation[style={tikzuml aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlVHuniaggreg}[3][]{\umlVHrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlVHcompo}[3][]{\umlVHrelation[style={tikzuml composition style}, #1]{#2}{#3}}% +\newcommand{\umlVHunicompo}[3][]{\umlVHrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}}% +\newcommand{\umlVHimport}[3][]{\umlVHrelation[style={tikzuml import style}, #1]{#2}{#3}}% +\newcommand{\umlVHnest}[3][]{\umlVHrelation[style={tikzuml nesting style}, #1]{#2}{#3}}% +\newcommand{\umlVHdep}[3][]{\umlVHrelation[style={tikzuml dependency style}, #1]{#2}{#3}}% +\newcommand{\umlVHfriend}[3][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHfriend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHfriend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlrelation[geometry=|-, stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVHinherit}[3][]{\umlHVHrelation[style={tikzuml inherit style}, #1]{#2}{#3}}% +\newcommand{\umlHVHimpl}[3][]{\umlHVHrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlHVHreal}[3][]{\umlHVHrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlHVHassoc}[3][]{\umlHVHrelation[style={tikzuml association style}, #1]{#2}{#3}}% +\newcommand{\umlHVHuniassoc}[3][]{\umlHVHrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umlHVHaggreg}[3][]{\umlHVHrelation[style={tikzuml aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlHVHuniaggreg}[3][]{\umlHVHrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlHVHcompo}[3][]{\umlHVHrelation[style={tikzuml composition style}, #1]{#2}{#3}}% +\newcommand{\umlHVHunicompo}[3][]{\umlHVHrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}}% +\newcommand{\umlHVHimport}[3][]{\umlHVHrelation[style={tikzuml import style}, #1]{#2}{#3}}% +\newcommand{\umlHVHnest}[3][]{\umlHVHrelation[style={tikzuml nesting style}, #1]{#2}{#3}}% +\newcommand{\umlHVHdep}[3][]{\umlHVHrelation[style={tikzuml dependency style}, #1]{#2}{#3}}% +\newcommand{\umlHVHfriend}[3][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVHfriend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHfriend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlrelation[geometry=-|-, stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHVinherit}[3][]{\umlVHVrelation[style={tikzuml inherit style}, #1]{#2}{#3}}% +\newcommand{\umlVHVimpl}[3][]{\umlVHVrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlVHVreal}[3][]{\umlVHVrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlVHVassoc}[3][]{\umlVHVrelation[style={tikzuml association style}, #1]{#2}{#3}}% +\newcommand{\umlVHVuniassoc}[3][]{\umlVHVrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umlVHVaggreg}[3][]{\umlVHVrelation[style={tikzuml aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlVHVuniaggreg}[3][]{\umlVHVrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlVHVcompo}[3][]{\umlVHVrelation[style={tikzuml composition style}, #1]{#2}{#3}}% +\newcommand{\umlVHVunicompo}[3][]{\umlVHVrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}}% +\newcommand{\umlVHVimport}[3][]{\umlVHVrelation[style={tikzuml import style}, #1]{#2}{#3}}% +\newcommand{\umlVHVnest}[3][]{\umlVHVrelation[style={tikzuml nesting style}, #1]{#2}{#3}}% +\newcommand{\umlVHVdep}[3][]{\umlVHVrelation[style={tikzuml dependency style}, #1]{#2}{#3}}% +\newcommand{\umlVHVfriend}[3][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHVfriend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVfriend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlrelation[geometry=|-|, stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +% define a node +\newcommand{\umlnode}[2]{% + \node (#2) at (#1) {};% +}% +% +% main command to define a relation between two classes through a control node +% args : src class +% control node +% dest class +% optional : arg1, arg2, arg: name of the src/dest/dest side class type attribute defined by the relation +% mult1, mult2, mult: multiplicity of the src/dest/dest side class type attribute defined by the relation +% pos1, pos2, pos: position of the src/dest/dest side class type attribute defined by the relation +% align1, align2, align: text justification of the src/dest/dest side class type attribute defined by the relation +% anchor1, anchor2: src/dest anchors on linked classes +% stereo: stereotype of the relation +% pos stereo: position of the stereotype on the relation +% style: style of the relation (association, aggregation, composition, inherit, ...) +% name: rootname used for naming nodes of the relation +\newcommand{\umlCNrelation}[4][]{% + \pgfkeys{/tikzuml/relation/.cd, arg1/.initial={}, arg2/.initial={}, arg/.initial={},% + mult1/.initial={}, mult2/.initial={}, mult/.initial={},% + pos1/.initial=0.2, pos2/.initial=0.8, pos/.initial=tikzumlEmpty,% + align1/.initial={}, align2/.initial={}, align/.initial={},% + anchor1/.initial=tikzumlEmpty, anchor2/.initial=tikzumlEmpty,% + stereo/.initial={}, pos stereo/.initial=1,% + style/.initial=->, name/.initial=relation-\thetikzumlRelationNum,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlCNrelation, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/relation/.cd,#1}% + \pgfkeys{/tikzuml/relation/.cd, arg1/.get=\tikzumlCNRelationAttrName, arg2/.get=\tikzumlCNRelationAttrNameTO, arg/.get=\tikzumlCNRelationAttrNameTT,% + mult1/.get=\tikzumlCNRelationMultiplicity, mult2/.get=\tikzumlCNRelationMultiplicityTO, mult/.get=\tikzumlCNRelationMultiplicityTT,% + pos1/.get=\tikzumlCNRelationPosition, pos2/.get=\tikzumlCNRelationPositionTO, pos/.get=\tikzumlCNRelationPositionTT,% + align1/.get=\tikzumlCNRelationAlign, align2/.get=\tikzumlCNRelationAlignTO, align/.get=\tikzumlCNRelationAlignTT,% + anchor1/.get=\tikzumlCNRelationSrcAnchor, anchor2/.get=\tikzumlCNRelationDestAnchor,% + stereo/.get=\tikzumlCNRelationStereoType, pos stereo/.get=\tikzumlCNRelationPositionStereotype,% + style/.get=\tikzumlCNRelationStyle, name/.get=\tikzumlCNRelationName% + }% + % + % managing \_ in class names for node names + \def\tikzumlSrcClassName{#2}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlSrcClassNodeName{\tikzumlSrcClassName}}\x% + % + \StrSubstitute{\tikzumlSrcClassNodeName}{:}{@COLON@}[\tikzumlSrcClassNodeName]% + \StrSubstitute{\tikzumlSrcClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlSrcClassNodeName]% + % + \def\tikzumlDestClassName{#4}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlDestClassNodeName{\tikzumlDestClassName}}\x% + % + \StrSubstitute{\tikzumlDestClassNodeName}{:}{@COLON@}[\tikzumlDestClassNodeName]% + \StrSubstitute{\tikzumlDestClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlDestClassNodeName]% + % + % managing alias keys + \def\tikzumlCNRelationAttrNameT{\tikzumlCNRelationAttrNameTO\tikzumlCNRelationAttrNameTT}% + \def\tikzumlCNRelationMultiplicityT{\tikzumlCNRelationMultiplicityTO\tikzumlCNRelationMultiplicityTT}% + \def\tikzumlCNRelationAlignT{\tikzumlCNRelationAlignTO\tikzumlCNRelationAlignTT}% + \def\orientationT{\orientationTO\orientationTT}% + % + \ifthenelse{\equal{\tikzumlCNRelationPositionTT}{tikzumlEmpty}}{% + \def\tikzumlCNRelationPositionT{\tikzumlCNRelationPositionTO}% + }{% + \def\tikzumlCNRelationPositionT{\tikzumlCNRelationPositionTT}% + }% + % + \def\attrAlign{}% + \def\multAlign{}% + \def\attrAlignT{}% + \def\multAlignT{}% + % + \ifthenelse{\equal{\tikzumlCNRelationAlign}{left}}{% + \def\attrAlign{above right}% + \def\multAlign{below right}% + }{% + \ifthenelse{\equal{\tikzumlCNRelationAlign}{right}}{% + \def\attrAlign{above left}% + \def\multAlign{below left}% + }{}% + }% + % + \ifthenelse{\equal{\tikzumlCNRelationAlignT}{left}}{% + \def\attrAlignT{above right}% + \def\multAlignT{below right}% + }{% + \ifthenelse{\equal{\tikzumlCNRelationAlignT}{right}}{% + \def\attrAlignT{above left}% + \def\multAlignT{below left}% + }{}% + }% + % + % def stereotype + \ifthenelse{\equal{\tikzumlCNRelationStereoType}{}}{% + \def\stereotype{}% + }{% + \def\stereotype{$\ll$\tikzumlCNRelationStereoType$\gg$}% + }% + % + % def anchors macros + \ifthenelse{\equal{\tikzumlCNRelationSrcAnchor}{tikzumlEmpty}}{% + \def\tikzumlCNRelationSrcAnchor{}% + }{% + \let\tikzumlCNRelationSrcAnchorold\tikzumlCNRelationSrcAnchor% + \def\tikzumlCNRelationSrcAnchor{.\tikzumlCNRelationSrcAnchorold}% + }% + % + \ifthenelse{\equal{\tikzumlCNRelationDestAnchor}{tikzumlEmpty}}{% + \def\tikzumlCNRelationDestAnchor{}% + }{% + \let\tikzumlCNRelationDestAnchorold\tikzumlCNRelationDestAnchor% + \def\tikzumlCNRelationDestAnchor{.\tikzumlCNRelationDestAnchorold}% + }% + % + \setcounter{pos}{100*\real{\tikzumlCNRelationPosition}}% + \setcounter{posT}{100*\real{\tikzumlCNRelationPositionT}}% + \setcounter{posStereo}{100*\real{\tikzumlCNRelationPositionStereotype}}% + % + % straight line + %\setcounter{tikzumlControlNodesNum}{1}% + % + \def\tikzumlFirstArc{node[midway, name=\tikzumlCNRelationName-1, anchor=center] {} }% + \def\tikzumlLastArc{node[midway, name=\tikzumlCNRelationName-3, anchor=center]{} }% + \def\posAttrName{}% + \def\posMultiplicity{}% + \def\posAttrNameT{}% + \def\posMultiplicityT{}% + % + \begin{pgfonlayer}{connections}% + \node (\tikzumlCNRelationName-2) at (#3) {};% + \end{pgfonlayer}% + % + \ifnum\theposStereo>100% + \pgfmathsetmacro{\tikzumlCNRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlCNRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlCNRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + % + \ifnum\thepos>100% + \pgfmathsetmacro{\tikzumlCNRelationPosition}{(\thepos-100)/100}% + \def\arcNum{2}% + \else% + \def\arcNum{1}% + \ifnum\thepos=100% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + \fi% + \fi% + % + \ifnum\arcNum=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlCNRelationPosition, \posAttrName, \attrAlign] {\tikzumlCNRelationAttrName}% + node[pos=\tikzumlCNRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlCNRelationMultiplicity} }% + \fi% + \ifnum\arcNum=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlCNRelationPosition, \posAttrName, \attrAlign] {\tikzumlCNRelationAttrName}% + node[pos=\tikzumlCNRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlCNRelationMultiplicity} }% + \fi% + % + \ifnum\theposT>100% + \pgfmathsetmacro{\tikzumlCNRelationPositionT}{(\theposT-100)/100}% + \def\arcNumT{2}% + \else% + \def\arcNumT{1}% + \ifnum\theposT=100% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + \fi% + \fi% + % + \ifnum\arcNumT=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlCNRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlCNRelationAttrNameT}% + node[pos=\tikzumlCNRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlCNRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlCNRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlCNRelationAttrNameT}% + node[pos=\tikzumlCNRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlCNRelationMultiplicityT} }% + \fi% + % + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlCNRelationSrcAnchor) -- \tikzumlFirstArc (\tikzumlCNRelationName-2.base) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlCNRelationDestAnchor) }% + + \begin{pgfonlayer}{connections}% + \ifthenelse{\equal{\tikzumlCNRelationStyle}{tikzuml nesting style}}{% + \pgfarrowsdeclare{nested}{nested}{...} + { + \tikzumlNestingSymbolSize=0.2pt% + \advance\tikzumlNestingSymbolSize by .5\pgflinewidth% + \pgfsetdash{}{0pt} % do not dash + \pgfsetroundjoin % fix join + \pgfsetroundcap % fix cap + \pgfpathmoveto{\pgfpoint{-16*\tikzumlNestingSymbolSize}{0pt}}% + \pgfpatharc{180}{90}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{90}{0}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{0}{-90}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{-90}{-180}{8*\tikzumlNestingSymbolSize}% + \pgfpathmoveto{\pgfpoint{-8*\tikzumlNestingSymbolSize}{8*\tikzumlNestingSymbolSize}}% + \pgfpathlineto{\pgfpoint{-8*\tikzumlNestingSymbolSize}{-8*\tikzumlNestingSymbolSize}}% + \pgfusepathqstroke% + }% + \draw[auto, \tikzumlCNRelationStyle, nested-, font=\tikzumlDefaultFont] \tikzumlPath ;% + }{ + \draw[auto, \tikzumlCNRelationStyle, font=\tikzumlDefaultFont] \tikzumlPath ;% + } + \end{pgfonlayer}% + % + \stepcounter{tikzumlRelationNum}% +}% +% +% shortcuts for cnrelations +\newcommand{\umlCNinherit}[4][]{\umlCNrelation[style={tikzuml inherit style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNimpl}[4][]{\umlCNrelation[style={tikzuml implements style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNreal}[4][]{\umlCNrelation[style={tikzuml implements style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNassoc}[4][]{\umlCNrelation[style={tikzuml association style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNuniassoc}[4][]{\umlCNrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNaggreg}[4][]{\umlCNrelation[style={tikzuml aggregation style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNuniaggreg}[4][]{\umlCNrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNcompo}[4][]{\umlCNrelation[style={tikzuml composition style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNunicompo}[4][]{\umlCNrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNimport}[4][]{\umlCNrelation[style={tikzuml import style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNnest}[4][]{\umlCNrelation[style={tikzuml nesting style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNdep}[4][]{\umlCNrelation[style={tikzuml dependency style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNfriend}[4][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlCNfriend, forbidden option stereo}% + }{}% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlCNrelation[stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}{#4}% +}% +% +% define a note +% arg : attached class +% label of the note +% optional : x,y: coordinates of the note +% width: width of the note +% geometry: geometry of the relation between the note and what it is about +% weight: barycentric weight for a 3-line relation +% arm: length of the first arm +% anchor1, anchor2: anchors of the relation +% draw, fill, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the note position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlnote}[3][]{% + \pgfkeys{/tikzuml/note/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlNoteDefaultWidth,% + geometry/.initial=\tikzumlRelationDefaultGeometry,% + weight/.initial=\tikzumlRelationDefaultWeight, arm/.initial=auto, style/.style={},% + anchor1/.initial=tikzumlEmpty, anchor2/.initial=tikzumlEmpty,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlNoteDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + no coords/.is if=tikzumlnoteWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/note/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/note/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/note/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlnote, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/note/.cd, #1}% + \pgfkeys{/tikzuml/note/.cd, x/.get=\tikzumlNoteX, y/.get=\tikzumlNoteY, width/.get=\tikzumlNoteTextWidth,% + geometry/.get=\tikzumlNoteGeometry,% + weight/.get=\tikzumlNoteWeight, arm/.get=\tikzumlNoteArm,% + anchor1/.get=\tikzumlNoteSrcAnchor, anchor2/.get=\tikzumlNoteDestAnchor,% + draw/.get=\tikzumlNoteDrawColor, fill/.get=\tikzumlNoteFillColor,% + text/.get=\tikzumlNoteTextColor% + }% + % + \def\tikzumlClassName{#2}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlClassNodeName{\tikzumlClassName}}\x% + % + % def anchors macros + \ifthenelse{\equal{\tikzumlNoteSrcAnchor}{tikzumlEmpty}}{% + \def\tikzumlNoteSrcAnchor{}% + }{% + \let\tikzumlNoteSrcAnchorold\tikzumlNoteSrcAnchor% + \def\tikzumlNoteSrcAnchor{.\tikzumlNoteSrcAnchorold}% + }% + % + \ifthenelse{\equal{\tikzumlNoteDestAnchor}{tikzumlEmpty}}{% + \def\tikzumlNoteDestAnchor{}% + }{% + \let\tikzumlNoteDestAnchorold\tikzumlNoteDestAnchor% + \def\tikzumlNoteDestAnchor{.\tikzumlNoteDestAnchorold}% + }% + % + \iftikzumlnoteWithoutCoords% + \node[text=\tikzumlNoteTextColor, text width=\tikzumlNoteTextWidth, font=\tikzumlDefaultFont, outer sep=0, inner xsep=1ex, inner ysep=3ex, /tikzuml/note/style] (note-\thetikzumlNoteNum-coord) {#3};% + \else% + \node[text=\tikzumlNoteTextColor, text width=\tikzumlNoteTextWidth, font=\tikzumlDefaultFont, outer sep=0, inner xsep=1ex, inner ysep=3ex, /tikzuml/note/style] (note-\thetikzumlNoteNum-coord) at (\tikzumlNoteX, \tikzumlNoteY) {#3};% + \fi% + \draw (note-\thetikzumlNoteNum-coord.north east) node[name=note-\thetikzumlNoteNum-right-top, below=2ex, coordinate] {};% + \draw (note-\thetikzumlNoteNum-coord.north east) node[name=note-\thetikzumlNoteNum-top-right, left=2ex, coordinate] {};% + \draw[draw=\tikzumlNoteDrawColor, fill=\tikzumlNoteFillColor] (note-\thetikzumlNoteNum-coord.south west) -- (note-\thetikzumlNoteNum-coord.south east) -- (note-\thetikzumlNoteNum-right-top.base) -- (note-\thetikzumlNoteNum-top-right.base) -- (note-\thetikzumlNoteNum-coord.north west) -- cycle;% + \node[text=\tikzumlNoteTextColor, text width=\tikzumlNoteTextWidth, outer sep=0, inner xsep=1ex, inner ysep=3ex, font=\tikzumlDefaultFont] (note-\thetikzumlNoteNum) at (note-\thetikzumlNoteNum-coord) {#3};% + \draw[draw=\tikzumlNoteDrawColor] (note-\thetikzumlNoteNum-right-top) -| (note-\thetikzumlNoteNum-top-right);% + % + \pgfmathsetmacro{\tikzumlNoteWeightT}{1.0-\tikzumlNoteWeight}% + \node (note-\thetikzumlNoteNum-middle) at (barycentric cs:note-\thetikzumlNoteNum-coord=\tikzumlNoteWeight,\tikzumlClassNodeName=\tikzumlNoteWeightT) {};% + % + \ifthenelse{\equal{\tikzumlNoteGeometry}{--}% + \OR\equal{\tikzumlNoteGeometry}{-|}% + \OR\equal{\tikzumlNoteGeometry}{|-}}{% + \edef\tikzumlnotepath{\tikzumlNoteGeometry} + }{% + \ifthenelse{\equal{\tikzumlNoteGeometry}{-|-}}{% + \ifthenelse{\equal{\tikzumlNoteArm}{auto}}{% + \edef\tikzumlnotepath{-- (note-\thetikzumlNoteNum-coord\tikzumlNoteSrcAnchor -| note-\thetikzumlNoteNum-middle.center) -- (note-\thetikzumlNoteNum-middle.center) -- (note-\thetikzumlNoteNum-middle.center |- \tikzumlClassNodeName\tikzumlNoteDestAnchor) --}% + }{% + \draw (note-\thetikzumlNoteNum-coord\tikzumlNoteSrcAnchor)+(\tikzumlNoteArm,0) node[name=note-\thetikzumlNoteNum-tmp] {}; + \edef\tikzumlnotepath{-- (note-\thetikzumlNoteNum-tmp.center) |-}% + }% + }{% + \ifthenelse{\equal{\tikzumlNoteGeometry}{|-|}}{% + \ifthenelse{\equal{\tikzumlNoteArm}{auto}}{% + \edef\tikzumlnotepath{-- (note-\thetikzumlNoteNum-coord\tikzumlNoteSrcAnchor |- note-\thetikzumlNoteNum-middle.center) -- (note-\thetikzumlNoteNum-middle.center) -- (note-\thetikzumlNoteNum-middle.center -| \tikzumlClassNodeName\tikzumlNoteDestAnchor) --}% + }{% + \draw (note-\thetikzumlNoteNum-coord\tikzumlNoteSrcAnchor)+(0,\tikzumlNoteArm) node[name=note-\thetikzumlNoteNum-tmp] {}; + \edef\tikzumlnotepath{-- (note-\thetikzumlNoteNum-tmp.center) -|}% + }% + + }{% + \errmessage{TIKZUML ERROR : Unknown geometry value !!! It should be in the following list : --, |-, -|, |-|, -|-}% + }% + }% + }% + % + \begin{pgfonlayer}{connections}% + \draw[dashed] (note-\thetikzumlNoteNum-coord\tikzumlNoteSrcAnchor) \tikzumlnotepath (\tikzumlClassNodeName\tikzumlNoteDestAnchor);% + \end{pgfonlayer}% + % + \stepcounter{tikzumlNoteNum}% +}% +% +% shortcuts for note with geometry +\newcommand{\umlHVnote}[3][]{% + \pgfkeys{/tikzuml/note/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVnote, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/note/.cd, #1}% + \umlnote[geometry=-|, #1]{#2}{#3}% +}% +\newcommand{\umlVHnote}[3][]{% + \pgfkeys{/tikzuml/note/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHnote, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/note/.cd, #1}% + \umlnote[geometry=|-, #1]{#2}{#3}% +}% +\newcommand{\umlVHVnote}[3][]{% + \pgfkeys{/tikzuml/note/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVnote, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/note/.cd, #1}% + \umlnote[geometry=|-|, #1]{#2}{#3}% +}% +\newcommand{\umlHVHnote}[3][]{% + \pgfkeys{/tikzuml/note/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHnote, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/note/.cd, #1}% + \umlnote[geometry=-|-, #1]{#2}{#3}% +}% +% +% define a uml association class (command) +% args : name of the class +% attributes of the class +% operations of the class +% optional : x,y: coordinates of the class +% width: width of the class node +% type: type of of class (class, interface, typedef, enum) +% template: template parameters +% name: name of the class node +% geometry: geometry of the line +% weight: barycentric weight of the middle part when geometry is a 3-line +% arm: length of first part when geometry id a 3-line +% anchor1, anchor2: src/dest anchors on linked classes +% style: style of the association class (association, aggregation, composition, inherit, ...) +% draw, fill, fill template, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the class position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlassocclass}[5][]{% + \pgfkeys{/tikzuml/assocclass/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, + width/.initial=\tikzumlClassDefaultWidth, type/.initial=\tikzumlClassDefaultType, style/.style={},% + template/.initial={}, name/.initial=tikzumlEmpty, geometry/.initial=\tikzumlRelationDefaultGeometry,% + weight/.initial=\tikzumlRelationDefaultWeight, arm/.initial=auto,% + anchor1/.initial=tikzumlEmpty, anchor2/.initial=tikzumlEmpty,% + draw/.initial=\tikzumlDefaultDrawColor,% + fill template/.initial=\tikzumlClassTemplateFillColorDefaultFillColor,% + fill/.initial=\tikzumlClassDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + no coords/.is if=tikzumlassocclassWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/assocclass/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/assocclass/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/assocclass/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlassocclass, invalid option \keyname}% + }% + }% + % + \pgfkeys{/tikzuml/assocclass/.cd,#1}% + \pgfkeys{/tikzuml/assocclass/.cd, x/.get=\tikzumlAssocClassX, y/.get=\tikzumlAssocClassY,% + width/.get=\tikzumlAssocClassMinimumWidth, type/.get=\tikzumlAssocClassTypeTmp,% + template/.get=\tikzumlAssocClassTemplateParam,% + name/.get=\tikzumlAssocClassName, geometry/.get=\tikzumlAssocClassGeometry,% + weight/.get=\tikzumlAssocClassWeight, arm/.get=\tikzumlAssocClassArm,% + anchor1/.get=\tikzumlAssocClassSrcAnchor,% + anchor2/.get=\tikzumlAssocClassDestAnchor,% + draw/.get=\tikzumlAssocClassDrawColor, fill/.get=\tikzumlAssocClassFillColor,% + text/.get=\tikzumlAssocClassTextColor, fill template/.get=\tikzumlAssocClassTemplateFillColor% + }% + % + \ifthenelse{\equal{\tikzumlAssocClassTypeTmp}{class}\OR\equal{\tikzumlAssocClassTypeTmp}{abstract}}{% + \def\tikzumlAssocClassType{}% + }{% + \def\tikzumlAssocClassType{$\ll$\tikzumlAssocClassTypeTmp$\gg$ \\}% + }% + % + \ifthenelse{\equal{\tikzumlAssocClassTemplateParam}{}}{% + \def\tikzumlAssocClassVPadding{}% + \def\tikzumlAssocClassHPadding{}% + }{% + \def\tikzumlAssocClassVPadding{\vspace{0.1em} \\}% + \def\tikzumlAssocClassHPadding{\hspace{0.5ex} $ $}% + }% + % + \def\tikzumlAssocClassName{#2}% + \def\tikzumlAssocClassRelationName{#3}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlAssocClassNodeName{\tikzumlAssocClassName}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlAssocClassRelationNodeName{\tikzumlAssocClassRelationName}}\x% + % + \ifthenelse{\equal{\tikzumlAssocClassName}{tikzumlEmpty}}{}{% + \def\tikzumlAssocClassNodeName{\tikzumlAssocClassName}% + }% + % + \StrSubstitute{\tikzumlAssocClassNodeName}{:}{@COLON@}[\tikzumlAssocClassNodeName]% + \StrSubstitute{\tikzumlAssocClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlAssocClassNodeName]% + % + \ifthenelse{\equal{\tikzumlAssocClassTypeTmp}{abstract}}{% + \let\tikzumlAssocClassNameOld\tikzumlAssocClassName% + \def\tikzumlAssocClassName{{\it \tikzumlAssocClassNameOld}}% + }{}% + % + \def\tikzumlAssocClassPos{\tikzumlAssocClassX,\tikzumlAssocClassY}% + \def\tikzumlAssocClassAttributes{#4}% + \def\tikzumlAssocClassOperations{#5}% + % + % def anchors macros + \ifthenelse{\equal{\tikzumlAssocClassSrcAnchor}{tikzumlEmpty}}{% + \def\tikzumlAssocClassSrcAnchor{}% + }{% + \let\tikzumlAssocClassSrcAnchorold\tikzumlAssocClassSrcAnchor% + \def\tikzumlAssocClassSrcAnchor{.\tikzumlAssocClassSrcAnchorold}% + }% + % + \ifthenelse{\equal{\tikzumlAssocClassDestAnchor}{tikzumlEmpty}}{% + \def\tikzumlAssocClassDestAnchor{}% + }{% + \let\tikzumlAssocClassDestAnchorold\tikzumlAssocClassDestAnchor% + \def\tikzumlAssocClassDestAnchor{.\tikzumlAssocClassDestAnchorold}% + }% + % + \iftikzumlassocclassWithoutCoords% + \node[tikzuml class style, draw=\tikzumlAssocClassDrawColor, fill=\tikzumlAssocClassFillColor, text=\tikzumlAssocClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlAssocClassMinimumWidth, /tikzuml/assocclass/style] (\tikzumlAssocClassNodeName) {\begin{tabular}{c}\tikzumlAssocClassVPadding \tikzumlAssocClassType \tikzumlAssocClassHPadding \textbf{\tikzumlAssocClassName} \tikzumlAssocClassHPadding \end{tabular}% + \nodepart{second}% + \begin{tabular}{l}% + \tikzumlAssocClassAttributes% + \end{tabular}% + \nodepart{third}% + \begin{tabular}{l}% + \tikzumlAssocClassOperations% + \end{tabular}% + };% + \else% + \node[tikzuml class style, draw=\tikzumlAssocClassDrawColor, fill=\tikzumlAssocClassFillColor, text=\tikzumlAssocClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlAssocClassMinimumWidth, /tikzuml/assocclass/style] (\tikzumlAssocClassNodeName) at (\tikzumlAssocClassPos) {\begin{tabular}{c}\tikzumlAssocClassVPadding \tikzumlAssocClassType \tikzumlAssocClassHPadding \textbf{\tikzumlAssocClassName} \tikzumlAssocClassHPadding \end{tabular}% + \nodepart{second}% + \begin{tabular}{l}% + \tikzumlAssocClassAttributes% + \end{tabular}% + \nodepart{third}% + \begin{tabular}{l}% + \tikzumlAssocClassOperations% + \end{tabular}% + };% + \fi% + % + \ifthenelse{\equal{\tikzumlAssocClassTemplateParam}{}}{}{% + \draw (\tikzumlAssocClassNodeName.north east) node[tikzuml template style, name=\tikzumlAssocClassNodeName-template, draw=\tikzumlAssocClassDrawColor, fill=\tikzumlAssocClassTemplateFillColor, text=\tikzumlAssocClassTextColor, font=\tikzumlDefaultFont] {\tikzumlAssocClassTemplateParam};% + }% + % + \pgfmathsetmacro{\tikzumlAssocClassWeightT}{1.0-\tikzumlAssocClassWeight} + \node (\tikzumlAssocClassNodeName-middle) at (barycentric cs:\tikzumlAssocClassNodeName=\tikzumlAssocClassWeight,\tikzumlAssocClassRelationNodeName=\tikzumlAssocClassWeightT) {};% + % + \ifthenelse{\equal{\tikzumlAssocClassGeometry}{--}\OR\equal{\tikzumlAssocClassGeometry}{-|}\OR\equal{\tikzumlAssocClassGeometry}{|-}}{% + \edef\tikzumlassocclasspath{\tikzumlAssocClassGeometry} + }{% + \ifthenelse{\equal{\tikzumlAssocClassGeometry}{-|-}}{% + \ifthenelse{\equal{\tikzumlAssocClassArm}{auto}}{% + \edef\tikzumlassocclasspath{-- (\tikzumlAssocClassNodeName\tikzumlAssocClassSrcAnchor -| \tikzumlAssocClassNodeName-middle.center) -- (\tikzumlAssocClassNodeName-middle.center) -- (\tikzumlAssocClassNodeName-middle.center |- \tikzumlAssocClassRelationNodeName\tikzumlAssocClassDestAnchor) --}% + }{% + \draw (\tikzumlAssocClassNodeName\tikzumlAssocClassSrcAnchor)+(\tikzumlAssocClassArm,0) node[name=\tikzumlAssocClassNodeName-tmp] {}; + \edef\tikzumlnotepath{-- (\tikzumlAssocClassNodeName-tmp.center) |-}% + }% + }{% + \ifthenelse{\equal{\tikzumlAssocClassGeometry}{|-|}}{% + \ifthenelse{\equal{\tikzumlAssocClassArm}{auto}}{% + \edef\tikzumlassocclasspath{-- (\tikzumlAssocClassNodeName\tikzumlAssocClassSrcAnchor |- \tikzumlAssocClassNodeName-middle.center) -- (\tikzumlAssocClassNodeName-middle.center) -- (\tikzumlAssocClassNodeName-middle.center -| \tikzumlAssocClassRelationNodeName\tikzumlAssocClassDestAnchor) --}% + }{% + \draw (\tikzumlAssocClassNodeName\tikzumlAssocClassSrcAnchor)+(0,\tikzumlAssocClassArm) node[name=\tikzumlAssocClassNodeName-tmp] {}; + \edef\tikzumlassocclasspath{-- (\thetikzumlAssocClassNodeName-tmp.center) -|}% + }% + + }{% + \errmessage{TIKZUML ERROR : Unknown geometry value !!! It should be in the following list : --, |-, -|, |-|, -|-}% + }% + }% + }% + % + \begin{pgfonlayer}{connections}% + \draw[dashed] (\tikzumlAssocClassNodeName\tikzumlAssocClassSrcAnchor) \tikzumlassocclasspath (\tikzumlAssocClassRelationNodeName\tikzumlAssocClassDestAnchor);% + \end{pgfonlayer}% + % + % add to fit + \ifnum\c@tikzumlPackageLevel>0% + \edef\tikzumlPackageFitOld{\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname}% + \ifthenelse{\equal{\tikzumlAssocClassTemplateParam}{}}{% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlAssocClassNodeName)(\tikzumlAssocClassNodeName-middle)}% + }{% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlAssocClassNodeName) (\tikzumlAssocClassNodeName-template)(\tikzumlAssocClassNodeName-middle)}% + }% + \stepcounter{tikzumlPackageClassNum}% + \fi% +}% +% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% use case diagrams % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\tikzstyle{tikzuml usecase style}=[ellipse, text centered]% +\tikzstyle{tikzuml actor style}=[ellipse, inner sep=0, outer sep=0]% +% +\newcounter{tikzumlSystemUseCaseNum}% +\newcounter{tikzumlSystemLevel}% +\newcounter{tikzumlUseCaseNum}% +\newcounter{tikzumlActorNum}% +% +\newif\iftikzumlusecaseWithoutCoords% +\newif\iftikzumlactorWithoutCoords% +% +% define a system +% arg : name +% optional : x, y: coordinates of the system +% draw, fill, text: colors +\newenvironment{umlsystem}[2][]{% + \gdef\tikzumlSystemFit{}% + \def\tikzumlSystemName{#2}% + \setcounter{tikzumlSystemUseCaseNum}{0}% + % + \pgfkeys{/tikzuml/system/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlSystemDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlsystem, invalid option \keyname}% + }% + }% + % + \pgfkeys{/tikzuml/system/.cd, #1}% + \pgfkeys{/tikzuml/system/.cd, x/.get=\tikzumlSystemXShift, y/.get=\tikzumlSystemYShift,% + draw/.get=\tikzumlSystemDrawColor, fill/.get=\tikzumlSystemFillColor,% + text/.get=\tikzumlSystemTextColor}% + % + \stepcounter{tikzumlSystemLevel}% + % + \begin{scope}[xshift=\tikzumlSystemXShift cm, yshift=\tikzumlSystemYShift cm]% +}{% + \addtocounter{tikzumlSystemLevel}{-1}% + % if contains no usecase, one define a fictive node to enable the fit option + \ifnum\c@tikzumlSystemUseCaseNum=0% + \node[inner xsep=10ex, inner ysep=1em] (\tikzumlSystemName-root) at (0,0) {};% + \xdef\tikzumlSystemFit{(\tikzumlSystemName-root)}% + \fi% + % + \begin{pgfonlayer}{background}% + \node[inner ysep=1em, inner xsep=2ex, fit = \tikzumlSystemFit] (\tikzumlSystemName-tmp) {};% + \node[text=\tikzumlSystemTextColor, font=\tikzumlDefaultFont] (\tikzumlSystemName-caption-tmp) at (\tikzumlSystemName-tmp.north) {\tikzumlSystemName};% + \node[draw=\tikzumlSystemDrawColor, fill=\tikzumlSystemFillColor, text=\tikzumlSystemTextColor, font=\tikzumlDefaultFont, inner ysep=1em, inner xsep=2ex, fit = (\tikzumlSystemName-tmp) (\tikzumlSystemName-caption-tmp)] (\tikzumlSystemName) {};% + \node[text=\tikzumlSystemTextColor, font=\tikzumlDefaultFont] (\tikzumlSystemName-caption) at (\tikzumlSystemName-caption-tmp.north) {\tikzumlSystemName};% + \end{pgfonlayer}% + \end{scope}% + % +}% +% +% define a use case +% arg : label of the use case +% optional : x, y: coordinates of the use case +% name: name of the node +% width: node width +% draw, fill, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the use case position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlusecase}[2][]{% + \stepcounter{tikzumlUseCaseNum}% + \pgfkeys{/tikzuml/usecase/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=auto,% + name/.initial=usecase-\thetikzumlUseCaseNum,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlUseCaseDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor, style/.style={},% + no coords/.is if=tikzumlusecaseWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/usecase/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/usecase/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/usecase/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlusecase, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/usecase/.cd, #1}% + \pgfkeys{/tikzuml/usecase/.cd, x/.get=\tikzumlUseCaseX, y/.get=\tikzumlUseCaseY, width/.get=\tikzumlUseCaseTextWidth,% + name/.get=\tikzumlUseCaseName,% + draw/.get=\tikzumlUseCaseDrawColor, fill/.get=\tikzumlUseCaseFillColor,% + text/.get=\tikzumlUseCaseTextColor% + }% + % + \def\tikzumlUseCaseText{#2}% + % + \def\tikzumlUseCasePos{\tikzumlUseCaseX,\tikzumlUseCaseY}% + % + \ifthenelse{\equal{\tikzumlUseCaseTextWidth}{auto}}{% + \iftikzumlusecaseWithoutCoords% + \node[tikzuml usecase style, draw=\tikzumlUseCaseDrawColor, fill=\tikzumlUseCaseFillColor, text=\tikzumlUseCaseTextColor, font=\tikzumlDefaultFont, /tikzuml/usecase/style] (\tikzumlUseCaseName) {\tikzumlUseCaseText};% + \else% + \node[tikzuml usecase style, draw=\tikzumlUseCaseDrawColor, fill=\tikzumlUseCaseFillColor, text=\tikzumlUseCaseTextColor, font=\tikzumlDefaultFont, /tikzuml/usecase/style] (\tikzumlUseCaseName) at (\tikzumlUseCasePos) {\tikzumlUseCaseText};% + \fi% + }{% + \iftikzumlusecaseWithoutCoords% + \node[tikzuml usecase style, draw=\tikzumlUseCaseDrawColor, fill=\tikzumlUseCaseFillColor, text=\tikzumlUseCaseTextColor, font=\tikzumlDefaultFont, text width=\tikzumlUseCaseTextWidth, /tikzuml/usecase/style] (\tikzumlUseCaseName) {\tikzumlUseCaseText};% + \else% + \node[tikzuml usecase style, draw=\tikzumlUseCaseDrawColor, fill=\tikzumlUseCaseFillColor, text=\tikzumlUseCaseTextColor, font=\tikzumlDefaultFont, text width=\tikzumlUseCaseTextWidth, /tikzuml/usecase/style] (\tikzumlUseCaseName) at (\tikzumlUseCasePos) {\tikzumlUseCaseText};% + \fi% + }% + % + % add to fit + \ifnum\c@tikzumlSystemLevel>0% + \let\tikzumlSystemFitOld\tikzumlSystemFit% + \xdef\tikzumlSystemFit{\tikzumlSystemFitOld (\tikzumlUseCaseName)}% + \stepcounter{tikzumlSystemUseCaseNum}% + \fi% +}% +% +% define the actor symbol +% optional : global tikzpicture styles +\newcommand{\picturedactor}[1]{% + \pgfkeys{/tikzuml/picactor/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/picactor/.cd,#1}% + \pgfkeys{/tikzuml/picactor/.cd, scale/.get=\tikzumlPicturedActorScale}% + % + \begin{tikzpicture}[#1]% + \coordinate (head) at (0,4ex);% + \coordinate (left-hand) at (-2ex,2ex);% + \coordinate (right-hand) at (2ex,2ex);% + \coordinate (left-foot) at (-2ex,-2ex);% + \coordinate (right-foot) at (2ex,-2ex);% + \coordinate (empty) at (0,-3ex);% + \draw (empty) (0,0) -- (head);% + \draw (left-hand) -- (right-hand);% + \draw (0,0) -- (left-foot) (0,0) -- (right-foot);% + \node[fill, draw, circle, inner sep=\tikzumlPicturedActorScale*0.3333ex, minimum size=\tikzumlPicturedActorScale*2ex, anchor=base] at (head) {};% + \end{tikzpicture}% +}% +% +% define an actor +% arg : var name +% optional : x, y: coordinates of the actor +% scale: scale factor of the actor symbol +% below: distance between the actor symbol and its name below +% draw, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the actor position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlactor}[2][]{% + \stepcounter{tikzumlActorNum}% + \pgfkeys{/tikzuml/actor/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, scale/.initial=1, below/.initial=\tikzumlActorDefaultBelow,% + draw/.initial=\tikzumlDefaultDrawColor, text/.initial=\tikzumlDefaultTextColor,% + style/.style={},% + no coords/.is if=tikzumlactorWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/actor/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/actor/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/actor/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlactor, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/actor/.cd, #1}% + \pgfkeys{/tikzuml/actor/.cd,% + x/.get=\tikzumlActorX, y/.get=\tikzumlActorY, scale/.get=\tikzumlActorScale,% + below/.get=\tikzumlActorBelow,% + draw/.get=\tikzumlActorDrawColor, text/.get=\tikzumlActorTextColor}% + % + \def\tikzumlActorName{#2}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlActorNodeName{\tikzumlActorName}}\x% + % + \def\tikzumlActorPos{\tikzumlActorX,\tikzumlActorY}% + % + \iftikzumlactorWithoutCoords% + \node[tikzuml actor style, text=\tikzumlActorTextColor, font=\tikzumlDefaultFont, /tikzuml/actor/style] (\tikzumlActorNodeName) {\picturedactor{scale=\tikzumlActorScale, fill=white, draw=\tikzumlActorDrawColor, thick}};% + \else% + \node[tikzuml actor style, text=\tikzumlActorTextColor, font=\tikzumlDefaultFont, /tikzuml/actor/style] (\tikzumlActorNodeName) at (\tikzumlActorPos) {\picturedactor{scale=\tikzumlActorScale, fill=white, draw=\tikzumlActorDrawColor, thick}};% + \fi% + \node[text=\tikzumlActorTextColor, font=\tikzumlDefaultFont, below=\tikzumlActorScale*\tikzumlActorBelow] at (\tikzumlActorNodeName) {\tikzumlActorName};% + % +}% + +% shortcuts for include and extend relation +\newcommand{\umlinclude}[3][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlinclude, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlinclude, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlrelation[stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +\newcommand{\umlextend}[3][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlextend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlextend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlrelation[stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVinclude}[3][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVinclude, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVinclude, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlrelation[geometry=-|, stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +\newcommand{\umlHVextend}[3][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVextend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVextend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlrelation[geometry=-|, stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHinclude}[3][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHinclude, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHinclude, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlrelation[geometry=|-, stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +\newcommand{\umlVHextend}[3][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR :in umlVHextend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHextend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlrelation[geometry=|-, stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVHinclude}[3][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVHinclude, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHinclude, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlrelation[geometry=-|-, stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +\newcommand{\umlHVHextend}[3][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVHextend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHextend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlrelation[geometry=-|-, stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHVinclude}[3][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHVinclude, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVinclude, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlrelation[geometry=|-|, stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +\newcommand{\umlVHVextend}[3][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHVextend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVextend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlrelation[geometry=|-|, stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlCNinclude}[4][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlCNinclude, forbidden option stereo}% + }{}% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlCNrelation[stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}{#4}% +}% +\newcommand{\umlCNextend}[4][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlCNextend, forbidden option stereo}% + }{}% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlCNrelation[stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}{#4}% +}% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% state diagrams % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\tikzstyle{tikzuml state style}=[rectangle split, rectangle split parts=2, rounded corners, inner xsep=1.5ex]% +\tikzstyle{tikzuml transition style}=[color=\tikzumlDefaultDrawColor, rounded corners, -angle 45]% +% +\newcounter{tikzumlStateJoinNum}% +\newcounter{tikzumlStateDecisionNum}% +\newcounter{tikzumlStateInitialNum}% +\newcounter{tikzumlStateFinalNum}% +\newcounter{tikzumlStateEnterNum}% +\newcounter{tikzumlStateExitNum}% +\newcounter{tikzumlStateEndNum}% +\newcounter{tikzumlStateHistoryNum}% +\newcounter{tikzumlStateDeepHistoryNum}% +\newcounter{tikzumlStateLevel}% +\newcounter{tikzumlStateSubStateNum}% +\newcounter{tikzumlStateText}% +% +\newif\iftikzumlstatejoinWithoutCoords% +\newif\iftikzumlstatedecisionWithoutCoords% +\newif\iftikzumlstateinitialWithoutCoords% +\newif\iftikzumlstatefinalWithoutCoords% +\newif\iftikzumlstateenterWithoutCoords% +\newif\iftikzumlstateexitWithoutCoords% +\newif\iftikzumlstateendWithoutCoords% +\newif\iftikzumlstatehistoryWithoutCoords% +\newif\iftikzumlstatedeephistoryWithoutCoords% +\newif\iftikzumlstateWithoutCoords% +% +% define a uml join state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstatejoin}[1][]{% + \pgfkeys{/tikzuml/statejoin/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateJoinDefaultWidth,% + name/.initial=statejoin-\thetikzumlStateJoinNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstatejoinWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/statejoin/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/statejoin/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/statejoin/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstatejoin, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/statejoin/.cd, #1}% + \pgfkeys{/tikzuml/statejoin/.cd, x/.get=\tikzumlStateJoinX, y/.get=\tikzumlStateJoinY, width/.get=\tikzumlStateJoinMinimumWidth,% + name/.get=\tikzumlStateJoinName, color/.get=\tikzumlStateJoinColor% + }% + % + \def\tikzumlStateJoinPos{\tikzumlStateJoinX,\tikzumlStateJoinY}% + % + \iftikzumlstatejoinWithoutCoords% + \node[circle, minimum size=\tikzumlStateJoinMinimumWidth, draw=\tikzumlStateJoinColor, fill=\tikzumlStateJoinColor, /tikzuml/statejoin/style] (\tikzumlStateJoinName) {};% + \else% + \node[circle, minimum size=\tikzumlStateJoinMinimumWidth, draw=\tikzumlStateJoinColor, fill=\tikzumlStateJoinColor, /tikzuml/statejoin/style] (\tikzumlStateJoinName) at (\tikzumlStateJoinPos) {};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateJoinName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateJoinNum}% +}% +% +% define a uml decision state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstatedecision}[1][]{% + \pgfkeys{/tikzuml/statedecision/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateDecisionDefaultWidth,% + name/.initial=statedecision-\thetikzumlStateDecisionNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstatedecisionWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/statedecision/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/statedecision/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/statedecision/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstatedecision, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/statedecision/.cd, #1}% + \pgfkeys{/tikzuml/statedecision/.cd, x/.get=\tikzumlStateDecisionX, y/.get=\tikzumlStateDecisionY, width/.get=\tikzumlStateDecisionMinimumWidth,% + name/.get=\tikzumlStateDecisionName, color/.get=\tikzumlStateDecisionColor% + }% + % + \def\tikzumlStateDecisionPos{\tikzumlStateDecisionX,\tikzumlStateDecisionY}% + % + \iftikzumlstatedecisionWithoutCoords% + \node[rectangle, rotate=45, minimum size=\tikzumlStateDecisionMinimumWidth, draw=\tikzumlStateDecisionColor, /tikzuml/statedecision/style] (\tikzumlStateDecisionName) {};% + \else% + \node[rectangle, rotate=45, minimum size=\tikzumlStateDecisionMinimumWidth, draw=\tikzumlStateDecisionColor, /tikzuml/statedecision/style] (\tikzumlStateDecisionName) at (\tikzumlStateDecisionPos) {};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateDecisionName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateDecisionNum}% +}% +% +% define a uml initial state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% entry, do, exit: entry/do/exit action of the state +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstateinitial}[1][]{% + \pgfkeys{/tikzuml/stateinitial/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateInitialDefaultWidth,% + name/.initial=stateinitial-\thetikzumlStateInitialNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstateinitialWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/stateinitial/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/stateinitial/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/stateinitial/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstateinitial, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/stateinitial/.cd, #1}% + \pgfkeys{/tikzuml/stateinitial/.cd, x/.get=\tikzumlStateInitialX, y/.get=\tikzumlStateInitialY, width/.get=\tikzumlStateInitialMinimumWidth,% + name/.get=\tikzumlStateInitialName, color/.get=\tikzumlStateInitialColor% + }% + % + \def\tikzumlStateInitialPos{\tikzumlStateInitialX,\tikzumlStateInitialY}% + % + \iftikzumlstateinitialWithoutCoords% + \node[circle, minimum size=\tikzumlStateInitialMinimumWidth, fill=\tikzumlStateInitialColor, /tikzuml/stateinitial/style] (\tikzumlStateInitialName) {};% + \else% + \node[circle, minimum size=\tikzumlStateInitialMinimumWidth, fill=\tikzumlStateInitialColor, /tikzuml/stateinitial/style] (\tikzumlStateInitialName) at (\tikzumlStateInitialPos) {};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateInitialName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateInitialNum}% +}% +% +% define a uml final state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstatefinal}[1][]{% + \pgfkeys{/tikzuml/statefinal/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateFinalDefaultWidth,% + name/.initial=statefinal-\thetikzumlStateFinalNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstatefinalWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/statefinal/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/statefinal/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/statefinal/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstatefinal, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/statefinal/.cd, #1}% + \pgfkeys{/tikzuml/statefinal/.cd, x/.get=\tikzumlStateFinalX, y/.get=\tikzumlStateFinalY, width/.get=\tikzumlStateFinalMinimumWidth,% + name/.get=\tikzumlStateFinalName, color/.get=\tikzumlStateFinalColor% + }% + % + \def\tikzumlStateFinalPos{\tikzumlStateFinalX,\tikzumlStateFinalY}% + % + \iftikzumlstatefinalWithoutCoords% + \node[circle, minimum size=\tikzumlStateFinalMinimumWidth, draw=\tikzumlStateFinalColor, fill=\tikzumlStateFinalColor, double, double distance=0.1cm, /tikzuml/statefinal/style] (\tikzumlStateFinalName) {};% + \else% + \node[circle, minimum size=\tikzumlStateFinalMinimumWidth, draw=\tikzumlStateFinalColor, fill=\tikzumlStateFinalColor, double, double distance=0.1cm, /tikzuml/statefinal/style] (\tikzumlStateFinalName) at (\tikzumlStateFinalPos) {};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateFinalName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateFinalNum}% +}% +% +% define a uml enter state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstateenter}[1][]{% + \pgfkeys{/tikzuml/stateenter/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateEnterDefaultWidth,% + name/.initial=stateenter-\thetikzumlStateEnterNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstateenterWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/stateenter/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/stateenter/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/stateenter/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstateenter, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/stateenter/.cd, #1}% + \pgfkeys{/tikzuml/stateenter/.cd, x/.get=\tikzumlStateEnterX, y/.get=\tikzumlStateEnterY, width/.get=\tikzumlStateEnterMinimumWidth,% + name/.get=\tikzumlStateEnterName, color/.get=\tikzumlStateEnterColor% + }% + % + \def\tikzumlStateEnterPos{\tikzumlStateEnterX,\tikzumlStateEnterY}% + % + \iftikzumlstateenterWithoutCoords% + \node[circle, minimum size=\tikzumlStateEnterMinimumWidth, draw=\tikzumlStateEnterColor, /tikzuml/stateenter/style] (\tikzumlStateEnterName) {};% + \else% + \node[circle, minimum size=\tikzumlStateEnterMinimumWidth, draw=\tikzumlStateEnterColor, /tikzuml/stateenter/style] (\tikzumlStateEnterName) at (\tikzumlStateEnterPos) {};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateEnterName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateEnterNum}% +}% +% +% define a uml exit state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstateexit}[1][]{% + \pgfkeys{/tikzuml/stateexit/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateExitDefaultWidth,% + name/.initial=stateexit-\thetikzumlStateExitNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstateexitWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/stateexit/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/stateexit/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/stateexit/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstateexit, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/stateexit/.cd, #1}% + \pgfkeys{/tikzuml/stateexit/.cd, x/.get=\tikzumlStateExitX, y/.get=\tikzumlStateExitY, width/.get=\tikzumlStateExitMinimumWidth,% + name/.get=\tikzumlStateExitName, color/.get=\tikzumlStateExitColor% + }% + % + \def\tikzumlStateExitPos{\tikzumlStateExitX,\tikzumlStateExitY}% + % + \iftikzumlstateexitWithoutCoords% + \node[circle, minimum size=\tikzumlStateExitMinimumWidth, draw=\tikzumlStateExitColor, /tikzuml/stateexit/style] (\tikzumlStateExitName) {};% + \else% + \node[circle, minimum size=\tikzumlStateExitMinimumWidth, draw=\tikzumlStateExitColor, /tikzuml/stateexit/style] (\tikzumlStateExitName) at (\tikzumlStateExitPos) {};% + \fi% + \draw[draw=\tikzumlStateExitColor] (\tikzumlStateExitName.north east) -- (\tikzumlStateExitName.south west) (\tikzumlStateExitName.north west) -- (\tikzumlStateExitName.south east); + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateExitName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateExitNum}% +}% +% +% define a uml end state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstateend}[1][]{% + \pgfkeys{/tikzuml/stateend/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateEndDefaultWidth,% + name/.initial=stateend-\thetikzumlStateEndNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstateendWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/stateend/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/stateend/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/stateend/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstateend, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/stateend/.cd, #1}% + \pgfkeys{/tikzuml/stateend/.cd, x/.get=\tikzumlStateEndX, y/.get=\tikzumlStateEndY, width/.get=\tikzumlStateEndMinimumWidth,% + name/.get=\tikzumlStateEndName, color/.get=\tikzumlStateEndColor% + }% + % + \def\tikzumlStateEndPos{\tikzumlStateEndX,\tikzumlStateEndY}% + % + \iftikzumlstateendWithoutCoords% + \node[circle, minimum size=\tikzumlStateEndMinimumWidth, /tikzuml/stateend/style] (\tikzumlStateEndName) {};% + \else% + \node[circle, minimum size=\tikzumlStateEndMinimumWidth, /tikzuml/stateend/style] (\tikzumlStateEndName) at (\tikzumlStateEndPos) {};% + \fi% + \draw[draw=\tikzumlStateEndColor] (\tikzumlStateEndName.north east) -- (\tikzumlStateEndName.south west) (\tikzumlStateEndName.north west) -- (\tikzumlStateEndName.south east); + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateEndName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateEndNum}% +}% +% +\newcommand{\picturedhistory}[1]{% + \begin{tikzpicture}[#1]% + \draw[thick] (-0.1cm,-0.15cm) -- (-0.1cm,0.15cm) + (-0.1cm,0) -- (0.1cm,0) + (0.1cm,-0.15cm) -- (0.1cm,0.15cm);% + \end{tikzpicture}% +}% +% +% define a uml history state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstatehistory}[1][]{% + \pgfkeys{/tikzuml/statehistory/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateHistoryDefaultWidth,% + name/.initial=statehistory-\thetikzumlStateHistoryNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstatehistoryWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/statehistory/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/statehistory/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/statehistory/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstatehistory, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/statehistory/.cd, #1}% + \pgfkeys{/tikzuml/statehistory/.cd, x/.get=\tikzumlStateHistoryX, y/.get=\tikzumlStateHistoryY, width/.get=\tikzumlStateHistoryMinimumWidth,% + name/.get=\tikzumlStateHistoryName, color/.get=\tikzumlStateHistoryColor% + }% + % + \def\tikzumlStateHistoryPos{\tikzumlStateHistoryX,\tikzumlStateHistoryY}% + % + \iftikzumlstatehistoryWithoutCoords% + \node[circle, minimum size=\tikzumlStateHistoryMinimumWidth, draw=\tikzumlStateHistoryColor, /tikzuml/statehistory/style] (\tikzumlStateHistoryName) {\picturedhistory{draw=\tikzumlStateHistoryColor}};% + \else% + \node[circle, minimum size=\tikzumlStateHistoryMinimumWidth, draw=\tikzumlStateHistoryColor, /tikzuml/statehistory/style] (\tikzumlStateHistoryName) at (\tikzumlStateHistoryPos) {\picturedhistory{draw=\tikzumlStateHistoryColor}};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateHistoryName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateHistoryNum}% +}% +% +\newcommand{\pictureddeephistory}[1]{% + \begin{tikzpicture}[#1]% + \draw[thick] (-0.1cm,-0.15cm) -- (-0.1cm,0.15cm) + (-0.1cm,0) -- (0.1cm,0) + (0.1cm,-0.15cm) -- (0.1cm,0.15cm) + (0.23cm,0.19cm) -- (0.23cm,0.11cm) + (0.20cm,0.17cm) -- (0.26cm,0.13cm) + (0.20cm,0.13cm) -- (0.26cm,0.17cm);% + \end{tikzpicture}% +}% +% +% define a uml deep-history state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstatedeephistory}[1][]{% + \pgfkeys{/tikzuml/statedeephistory/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateDeepHistoryDefaultWidth,% + name/.initial=statedeephistory-\thetikzumlStateDeepHistoryNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstatedeephistoryWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/statedeephistory/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/statedeephistory/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/statedeephistory/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstatedeephistory, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/statedeephistory/.cd, #1}% + \pgfkeys{/tikzuml/statedeephistory/.cd, x/.get=\tikzumlStateDeepHistoryX, y/.get=\tikzumlStateDeepHistoryY, width/.get=\tikzumlStateDeepHistoryMinimumWidth,% + name/.get=\tikzumlStateDeepHistoryName, color/.get=\tikzumlStateDeepHistoryColor% + }% + % + \def\tikzumlStateDeepHistoryPos{\tikzumlStateDeepHistoryX,\tikzumlStateDeepHistoryY}% + % + \iftikzumlstatedeephistoryWithoutCoords% + \node[circle, minimum size=\tikzumlStateDeepHistoryMinimumWidth, draw=\tikzumlStateDeepHistoryColor, /tikzuml/statedeephistory/style] (\tikzumlStateDeepHistoryName) {\pictureddeephistory{draw=\tikzumlStateDeepHistoryColor}};% + \else% + \node[circle, minimum size=\tikzumlStateDeepHistoryMinimumWidth, draw=\tikzumlStateDeepHistoryColor, /tikzuml/statedeephistory/style] (\tikzumlStateDeepHistoryName) at (\tikzumlStateDeepHistoryPos) {\pictureddeephistory{draw=\tikzumlStateDeepHistoryColor}};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateDeepHistoryName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateDeepHistoryNum}% +}% +% +% define a uml state +% args : name of the state +% content of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% entry, do, exit: entry/do/exit action of the state +% draw, fill, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newenvironment{umlstate}[2][]{% + \ifnum\thetikzumlStateLevel>0% + \let\tikzumlState@nameold\tikzumlState@fitname% + \let\tikzumlState@parentold\tikzumlState@parent% + \edef\tikzumlState@parent{\tikzumlState@parentold @@\tikzumlState@nameold}% + \else% + \def\tikzumlState@parent{}% + \fi% + % + \stepcounter{tikzumlStateLevel}% + % + \pgfkeys{/tikzuml/state/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateDefaultWidth,% + name/.initial={},% + entry/.initial={}, do/.initial={}, exit/.initial={},% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlStateDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor, style/.style={},% + no coords/.is if=tikzumlstateWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/state/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/state/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/state/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + % \errmessage{TIKZUML ERROR : in umlstate, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/state/.cd, #1}% + \pgfkeys{/tikzuml/state/.cd, x/.get=\tikzumlStateXShift, y/.get=\tikzumlStateYShift, width/.get=\tikzumlStateMinimumWidth, name/.get=\tikzumlStateName,% + entry/.get=\tikzumlStateEntry, do/.get=\tikzumlStateDo, exit/.get=\tikzumlStateExit,% + draw/.get=\tikzumlStateDrawColor, fill/.get=\tikzumlStateFillColor,% + text/.get=\tikzumlStateTextColor% + }% + % + \ifthenelse{\equal{\tikzumlStateName}{}}{% + \edef\tikzumlState@name{#2}% + }{% + \edef\tikzumlState@name{\tikzumlStateName}% + }% + % + \begingroup% + \def\_{@}\edef\x{\endgroup% + \def\noexpand\tikzumlState@fitname{\tikzumlState@name}}\x% + % + \let\tikzumlState@nodeNameold\tikzumlState@nodeName% + \def\tikzumlState@caption{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlState@nodeName{\tikzumlState@name}}\x% + % + \expandafter\gdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{}% + % + \setcounter{tikzumlStateSubStateNum}{0}% + \setcounter{tikzumlStateText}{0}% + % + \def\tikzumlStateText{tikzumlEmpty}% + \begin{scope}[xshift=\tikzumlStateXShift cm, yshift=\tikzumlStateYShift cm]% +}{% + % + \def\tikzumlstaterootlabel{\phantom{\tikzumlState@nodeName}}% + % + \def\tikzumlstaterootinnerysep{0.5ex}% + \def\tikzumlstatebodyinnerysep{2ex}% + % + \ifthenelse{\equal{\tikzumlStateEntry}{}}{}{% + \ifnum\c@tikzumlStateText=0% + \def\tikzumlStateText{entry/\tikzumlStateEntry}% + \else% + \let\tikzumlStateTextOld\tikzumlStateText% + \ifthenelse{\equal{\tikzumlStateText}{tikzumlEmpty}}{% + \def\tikzumlStateText{entry/\tikzumlStateEntry}% + }{% + \expandafter\def\expandafter\tikzumlStateText\expandafter{\tikzumlStateTextOld \\ entry/\tikzumlStateEntry}% + }% + \fi% + \setcounter{tikzumlStateText}{1}% + \ifnum\c@tikzumlStateSubStateNum=0% + \def\tikzumlstatebodyinnerysep{0}% + \def\tikzumlstaterootinnerysep{0}% + \fi% + }% + \ifthenelse{\equal{\tikzumlStateDo}{}}{}{% + \ifnum\c@tikzumlStateText=0% + \def\tikzumlStateText{do/\tikzumlStateDo}% + \else% + \let\tikzumlStateTextOld\tikzumlStateText% + \ifthenelse{\equal{\tikzumlStateText}{tikzumlEmpty}}{% + \def\tikzumlStateText{do/\tikzumlStateDo}% + }{% + \expandafter\def\expandafter\tikzumlStateText\expandafter{\tikzumlStateTextOld \\ do/\tikzumlStateDo}% + }% + \fi% + \setcounter{tikzumlStateText}{1}% + \ifnum\c@tikzumlStateSubStateNum=0% + \def\tikzumlstatebodyinnerysep{0}% + \def\tikzumlstaterootinnerysep{0}% + \fi% + }% + \ifthenelse{\equal{\tikzumlStateExit}{}}{}{% + \ifnum\c@tikzumlStateText=0% + \def\tikzumlStateText{exit/\tikzumlStateExit}% + \else% + \let\tikzumlStateTextOld\tikzumlStateText% + \ifthenelse{\equal{\tikzumlStateText}{tikzumlEmpty}}{% + \def\tikzumlStateText{exit/\tikzumlStateExit}% + }{% + \expandafter\def\expandafter\tikzumlStateText\expandafter{\tikzumlStateTextOld \\ exit/\tikzumlStateExit}% + }% + \fi% + \setcounter{tikzumlStateText}{1}% + \ifnum\c@tikzumlStateSubStateNum=0% + \def\tikzumlstatebodyinnerysep{0}% + \def\tikzumlstaterootinnerysep{0}% + \fi% + }% + % + \addtocounter{tikzumlStateLevel}{-1}% + \begin{pgfonlayer}{state\thetikzumlStateLevel}% + % + % if contains nothing, one define a fictive node to enable the fit option + \ifnum\c@tikzumlStateSubStateNum=0% + \iftikzumlstateWithoutCoords% + \node[inner ysep=\tikzumlstaterootinnerysep, minimum width=\tikzumlStateMinimumWidth, /tikzuml/state/style] (\tikzumlState@nodeName-root) {\tikzumlstaterootlabel};% + \else% + \node[inner ysep=\tikzumlstaterootinnerysep, minimum width=\tikzumlStateMinimumWidth, /tikzuml/state/style] (\tikzumlState@nodeName-root) at (0,0) {\tikzumlstaterootlabel};% + \fi% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{(\tikzumlState@nodeName-root)}% + \fi% + % + \ifnum\c@tikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent\endcsname{\tikzumlStateFitTmp (\tikzumlState@nodeName-body) (\tikzumlState@nodeName-caption)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + % + \node[inner xsep=2ex, inner ysep=\tikzumlstatebodyinnerysep, fit = \csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname, /tikzuml/state/style ] (\tikzumlState@nodeName-body) {};% + \def\tikzumlState@orig{body}% + \ifnum\c@tikzumlStateText=1% + \node[above=0] (\tikzumlState@nodeName-texttmp) at (\tikzumlState@nodeName-\tikzumlState@orig.north) {\begin{tabular}{l}\tikzumlStateText \end{tabular}};% + \def\tikzumlState@orig{texttmp}% + \fi% + \node[above] (\tikzumlState@nodeName-captiontmp) at (\tikzumlState@nodeName-\tikzumlState@orig.north) {\tikzumlState@caption};% + \ifnum\c@tikzumlStateText=1% + \node[rounded corners, draw=\tikzumlStateDrawColor, fill=\tikzumlStateFillColor, name=\tikzumlState@nodeName, fit=(\tikzumlState@nodeName-body) (\tikzumlState@nodeName-texttmp) (\tikzumlState@nodeName-captiontmp)] {};% + \else% + \node[rounded corners, draw=\tikzumlStateDrawColor, fill=\tikzumlStateFillColor, name=\tikzumlState@nodeName, fit=(\tikzumlState@nodeName-body) (\tikzumlState@nodeName-captiontmp)] {};% + \fi% + \ifnum\c@tikzumlStateText=1% + \node (\tikzumlState@nodeName-text) at (\tikzumlState@nodeName-texttmp) {\begin{tabular}{l}\tikzumlStateText \end{tabular}};% + \fi% + \node (\tikzumlState@nodeName-caption) at (\tikzumlState@nodeName-captiontmp) {\tikzumlState@caption};% + \draw (\tikzumlState@nodeName-caption.south -| \tikzumlState@nodeName.north west) -- (\tikzumlState@nodeName-caption.south -| \tikzumlState@nodeName.north east);% + \end{pgfonlayer}% + \end{scope}% +}% +% +% shortcut for empty state +\newcommand{\umlbasicstate}[2][]{\begin{umlstate}[#1]{#2}\end{umlstate}}% +% +% command to add text in a state, to be used inside umlstate environment +\newcommand{\umlstatetext}[1]{% + \def\tikzumlStateText{#1}% + \setcounter{tikzumlStateText}{1}% +}% +% +% shortcuts for state transitions macros +\newcommand{\umltrans}[3][]{% + \ifthenelse{\equal{#2}{#3}}{% + \umlrelation[style={tikzuml transition style}, recursive mode=transition, #1]{#2}{#3}% + }{% + \umlrelation[style={tikzuml transition style}, #1]{#2}{#3}% + }% +}% +\newcommand{\umlHVtrans}[3][]{\umlHVrelation[style={tikzuml transition style}, #1]{#2}{#3}}% +\newcommand{\umlVHtrans}[3][]{\umlVHrelation[style={tikzuml transition style}, #1]{#2}{#3}}% +\newcommand{\umlVHVtrans}[3][]{\umlVHVrelation[style={tikzuml transition style}, #1]{#2}{#3}}% +\newcommand{\umlHVHtrans}[3][]{\umlHVHrelation[style={tikzuml transition style}, #1]{#2}{#3}}% +\newcommand{\umlCNtrans}[4][]{\umlCNrelation[style={tikzuml transition style}, #1]{#2}{#3}{#4}}% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% sequence diagrams % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\tikzstyle{tikzuml synchron-msg style}=[color=\tikzumlDefaultDrawColor, -triangle 45]% +\tikzstyle{tikzuml asynchron-msg style}=[color=\tikzumlDefaultDrawColor, -angle 45]% +\tikzstyle{tikzuml return-msg style}=[color=\tikzumlDefaultDrawColor, dashed, -angle 45]% +\tikzstyle{tikzuml call return style}=[color=\tikzumlDefaultDrawColor, dashed, -angle 45]% +\tikzstyle{tikzuml activity style}=[inner xsep=1ex, inner ysep=1ex]% +% +\newcounter{tikzumlObjectNum}% +\newcounter{tikzumlCallLevel}% +\newcounter{tikzumlCallNum}% +\newcounter{tikzumlFragmentLevel}% +\newcounter{tikzumlFragmentLevelNum}% +\newcounter{tikzumlFragmentNum}% +\newcounter{tikzumlFragmentPartNum}% +\newcounter{tikzumlCallStartFragmentNum}% +\newcounter{tikzumlCallEndFragmentNum}% +% +\newif\iftikzumlobjectNoDDots% +\newif\iftikzumlcreatecallNoDDots% +% +% define a sequence diagram +% +\newenvironment{umlseqdiag}{% + \gdef\tikzumlInCreateCall{0}% + \setcounter{tikzumlObjectNum}{0}% + \setcounter{tikzumlCallLevel}{0}% + \setcounter{tikzumlCallNum}{0}% + \setcounter{tikzumlFragmentLevel}{0}% + \setcounter{tikzumlFragmentLevelNum}{0}% + \setcounter{tikzumlFragmentNum}{0}% + \setcounter{tikzumlFragmentPartNum}{0}% + \setcounter{tikzumlCallStartFragmentNum}{0}% + \setcounter{tikzumlCallEndFragmentNum}{0}% + % + \ifx \@umlactor \@empty + \newcommand{\umlactor}[2][]{% + \pgfkeys{/tikzuml/actorobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlactor, forbidden option stereo}% + }{}% + }% + }% + % + \pgfkeys{/tikzuml/actorobj/.cd, ##1}% + \umlobject[stereo=actor, ##1]{##2}% + }% + \else% + \renewcommand{\umlactor}[2][]{% + \pgfkeys{/tikzuml/actorobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlactor, forbidden option stereo}% + }{}% + }% + }% + % + \pgfkeys{/tikzuml/actorobj/.cd, ##1}% + \umlobject[stereo=actor, ##1]{##2}% + }% + \fi% + \begin{scope}[font=\tikzumlDefaultFont]% +}{% + % draw lifelines of each object + \begin{pgfonlayer}{lifelines}% + \foreach \id in \tikzumlIdList {% + \draw (\csname tikzumlLastChild@\id \endcsname)+(0,-2.5ex) node[inner sep=0, name=end-\id] {};% + \draw[dotted] (\id) -- (end-\id);% + }% + \end{pgfonlayer}% + \end{scope}% +}% +% +% define the database symbol +% optional : global tikzpicture styles +\newcommand{\pictureddatabase}[1]{% + \pgfkeys{/tikzuml/database/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/database/.cd,#1}% + \pgfkeys{/tikzuml/database/.cd, scale/.get=\tikzumlDatabaseScale}% + % + \begin{tikzpicture}[#1]% + \node[fill, draw, ellipse, minimum width=\tikzumlDatabaseScale*4ex, minimum height=\tikzumlDatabaseScale*2ex, inner sep=0] (bottom) at (0,-2ex) {};% + \node[fill, draw, ellipse, minimum width=\tikzumlDatabaseScale*4ex, minimum height=\tikzumlDatabaseScale*2ex, inner sep=0] (top) at (0,4ex) {};% + \fill (bottom.west) rectangle (top.east);% + \begin{scope}% + \clip (-3.5ex,-0.5ex) rectangle (3.5ex,2.5ex);% + \node[draw, dashed, ellipse, minimum width=\tikzumlDatabaseScale*4ex, minimum height=\tikzumlDatabaseScale*2ex, inner sep=0] (bottom2) at (0,-2ex) {};% + \end{scope}% + \node[draw, ellipse, minimum width=\tikzumlDatabaseScale*4ex, minimum height=\tikzumlDatabaseScale*2ex, inner sep=0] (top2) at (0,4ex) {};% + \draw (bottom.west) -- (top.west) (bottom.east) -- (top.east);% + \end{tikzpicture}% +}% +% +% define the entity symbol +% optional : global tikzpicture styles +\newcommand{\picturedentity}[1]{% + \pgfkeys{/tikzuml/entity/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/entity/.cd,#1}% + \pgfkeys{/tikzuml/entity/.cd, scale/.get=\tikzumlEntityScale}% + % + \begin{tikzpicture}[#1]% + \node[fill, draw, circle, inner sep=0, minimum size=\tikzumlEntityScale*5ex] (center) at (0,0) {};% + \draw (center.south) node[coordinate, name=bottom] {};% + \draw (bottom)+(-2ex,0) node[coordinate, name=bottom-left] {};% + \draw (bottom)+(2ex,0) node[coordinate, name=bottom-right] {};% + \draw (center) -- (bottom);% + \draw (bottom-left) -- (bottom-right);% + \end{tikzpicture}% +}% +% +% define the boundary symbol +% optional : global tikzpicture styles +\newcommand{\picturedboundary}[1]{% + \pgfkeys{/tikzuml/boundary/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/boundary/.cd,#1}% + \pgfkeys{/tikzuml/boundary/.cd, scale/.get=\tikzumlBoundaryScale}% + % + \begin{tikzpicture}[#1] + \node[fill, draw, circle, inner sep=0, minimum size=\tikzumlBoundaryScale*5ex] (center) at (0,0) {}; + \draw (center.west)+(-0.8ex,0) node[coordinate, name=left] {}; + \draw (left)+(0,0.2ex) node[coordinate, name=left-top] {}; + \draw (left)+(0,-0.2ex) node[coordinate, name=left-bottom] {}; + \draw (center) -- (left); + \draw (left-top) -- (left-bottom); + \end{tikzpicture} +}% +% +% define the control symbol +% optional : global tikzpicture styles +\newcommand{\picturedcontrol}[1]{% + \pgfkeys{/tikzuml/control/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/control/.cd,#1}% + \pgfkeys{/tikzuml/control/.cd, scale/.get=\tikzumlControlScale}% + % + \begin{tikzpicture}[#1, decoration={markings, mark=at position 0.25 with {\arrow{>}}}] + \node[fill, draw, circle, inner sep=0, minimum size=\tikzumlControlScale*5ex, postaction={decorate}] (center) at (0,0) {}; + \end{tikzpicture} +}% +% +% define a uml object for a sequence diagram +% args : name of the object +% optional : x, y: coordinates of the object +% stereo: stereotype of the object (object, actor, database, boundary, control, entity, multiobject) +% class: class of the object +% scale: scale factor of the object symbol +% draw, fill, text; colors +% no ddots: when used, disable printing of double dots +\newcommand{\umlobject}[2][]{ + \stepcounter{tikzumlObjectNum}% + % + \edef\tikzumlobject@ddot{:}% + \pgfkeys{/tikzuml/obj/.cd, x/.initial=tikzumlEmpty, y/.initial=\tikzumlDefaultX, stereo/.initial=\tikzumlObjectDefaultStereo,% + class/.initial={}, scale/.initial=1,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlObjectDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + no ddots/.is if=tikzumlobjectNoDDots,% + no ddots=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlobject, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/obj/.cd, #1}% + \pgfkeys{/tikzuml/obj/.cd, x/.get=\tikzumlObjectX, y/.get=\tikzumlObjectY,% + stereo/.get=\tikzumlObjectStereo, class/.get=\tikzumlObjectClass,% + scale/.get=\tikzumlObjectScale,% + draw/.get=\tikzumlObjectDrawColor, fill/.get=\tikzumlObjectFillColor,% + text/.get=\tikzumlObjectTextColor% + }% + % + \iftikzumlobjectNoDDots% + \edef\tikzumlobject@ddot{}% + \fi% + % + \ifthenelse{\equal{\tikzumlObjectX}{tikzumlEmpty}}{% + \pgfmathsetmacro{\tikzumlObjectX}{4*(\thetikzumlObjectNum-1)}% + }{}% + % + \def\tikzumlObjectName{#2}% + \expandafter\xdef\csname tikzumlLastChild@\tikzumlObjectName \endcsname{\tikzumlObjectName}% + % + \ifnum\thetikzumlObjectNum=1% + \xdef\tikzumlIdList{\tikzumlObjectName}% + \else% + \let\tikzumlIdListOld\tikzumlIdList% + \expandafter\xdef\expandafter\tikzumlIdList\expandafter{\tikzumlIdListOld,\tikzumlObjectName}% + \fi% + % + \tikzstyle{tikzuml object box style}=[rectangle, text=\tikzumlObjectTextColor, font=\tikzumlDefaultFont]% + % + \ifthenelse{\equal{\tikzumlObjectStereo}{object}}{% + \tikzstyle{tikzuml object box style}+=[draw=\tikzumlObjectDrawColor, fill=\tikzumlObjectFillColor]% + }{% + \ifthenelse{\equal{\tikzumlObjectStereo}{multi}}{% + \tikzstyle{tikzuml object box style}+=[fill=\tikzumlObjectFillColor]% + }{}% + }% + % + \ifnum\tikzumlInCreateCall=1% + \draw (\tikzumlCreateCallObjectSrc -| \tikzumlObjectX,0) node[tikzuml object box style] (\tikzumlObjectName) {\tikzumlObjectName\tikzumlobject@ddot\tikzumlObjectClass};% + \else% + \node[tikzuml object box style] (\tikzumlObjectName) at (\tikzumlObjectX,\tikzumlObjectY) {\tikzumlObjectName\tikzumlobject@ddot\tikzumlObjectClass};% + \fi% + % + \ifthenelse{\equal{\tikzumlObjectStereo}{multi}}{% + \draw (\tikzumlObjectName.north east)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-tr, coordinate] {}; + \draw (\tikzumlObjectName.north west)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-tl, coordinate] {}; + \draw (\tikzumlObjectName.south east)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-br, coordinate] {}; + \draw (\tikzumlObjectName-tr)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-ttr, coordinate] {}; + \draw (\tikzumlObjectName-tl)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-ttl, coordinate] {}; + \draw (\tikzumlObjectName-br)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-tbr, coordinate] {}; + \fill[fill=\tikzumlObjectFillColor] (\tikzumlObjectName-ttl |- \tikzumlObjectName.north) -- (\tikzumlObjectName-ttl) -- (\tikzumlObjectName-ttr) -- (\tikzumlObjectName-tbr) -- (\tikzumlObjectName-tbr -| \tikzumlObjectName.east) -- (\tikzumlObjectName.north east) -- (\tikzumlObjectName-ttl |- \tikzumlObjectName.north); + \draw[draw=\tikzumlObjectDrawColor] (\tikzumlObjectName-ttl |- \tikzumlObjectName.north) -- (\tikzumlObjectName-ttl) -- (\tikzumlObjectName-ttr) -- (\tikzumlObjectName-tbr) -- (\tikzumlObjectName-tbr -| \tikzumlObjectName.east); + \fill[fill=\tikzumlObjectFillColor] (\tikzumlObjectName-tl |- \tikzumlObjectName.north) -- (\tikzumlObjectName-tl) -- (\tikzumlObjectName-tr) -- (\tikzumlObjectName-br) -- (\tikzumlObjectName-br -| \tikzumlObjectName.east) -- (\tikzumlObjectName.north east) -- (\tikzumlObjectName-tl |- \tikzumlObjectName.north); + \draw[draw=\tikzumlObjectDrawColor] (\tikzumlObjectName-tl |- \tikzumlObjectName.north) -- (\tikzumlObjectName-tl) -- (\tikzumlObjectName-tr) -- (\tikzumlObjectName-br) -- (\tikzumlObjectName-br -| \tikzumlObjectName.east); + \draw[draw=\tikzumlObjectDrawColor] (\tikzumlObjectName.north west) rectangle (\tikzumlObjectName.south east); + }{% + \ifthenelse{\equal{\tikzumlObjectStereo}{object}}{}{% + \node[above=1ex, name=\tikzumlObjectName-picture] at (\tikzumlObjectName) {\csname pictured\tikzumlObjectStereo \endcsname{draw=\tikzumlObjectDrawColor, fill=\tikzumlObjectFillColor, scale=\tikzumlObjectScale}}; + }% + }% +}% +% +% shortcuts for objects +\newcommand{\umlbasicobject}[2][]{% + \pgfkeys{/tikzuml/basicobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{no ddots}}{% + \errmessage{TIKZUML ERROR : in umlbasicobject, forbidden option no ddots}% + }{}% + }% + }% + \pgfkeys{/tikzuml/basicobj/.cd, #1}% + \umlobject[no ddots, #1]{#2}% +}% +% +\newcommand{\umldatabase}[2][]{% + \pgfkeys{/tikzuml/databaseobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umldatabase, forbidden option stereo}% + }{}% + }% + }% + \pgfkeys{/tikzuml/databaseobj/.cd, #1}% + \umlobject[stereo=database, #1]{#2}% +}% +\newcommand{\umlentity}[2][]{% + \pgfkeys{/tikzuml/entityobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlentity, forbidden option stereo}% + }{}% + }% + }% + \pgfkeys{/tikzuml/entityobj/.cd, #1}% + \umlobject[stereo=entity, #1]{#2}% +}% +\newcommand{\umlcontrol}[2][]{% + \pgfkeys{/tikzuml/controlobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlcontrol, forbidden option stereo}% + }{}% + }% + }% + \pgfkeys{/tikzuml/controlobj/.cd, #1}% + \umlobject[stereo=control, #1]{#2}% +}% +\newcommand{\umlboundary}[2][]{% + \pgfkeys{/tikzuml/boundaryobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlboundary, forbidden option stereo}% + }{}% + }% + }% + \pgfkeys{/tikzuml/boundaryobj/.cd, #1}% + \umlobject[stereo=boundary, #1]{#2}% +}% +\newcommand{\umlmulti}[2][]{% + \pgfkeys{/tikzuml/multiobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlmulti, forbidden option stereo}% + }{}% + }% + }% + \pgfkeys{/tikzuml/multiobj/.cd, #1}% + \umlobject[stereo=multi, #1]{#2}% +}% +% +\newcounter{tikzumlSDNodeNum}% +% +% define a hidden node to lengthen lifeline of a object +% args : object node +% optional : dt: distance between the sdnode and the last call defined on the lifeline of the object +% name: name of the sdnode +\newcommand{\umlsdnode}[2][]{% + \pgfkeys{/tikzuml/sdnode/.cd, dt/.initial=0, name/.initial=tikzumlEmpty}% + \pgfkeys{/tikzuml/sdnode/.cd, #1}% + \pgfkeys{/tikzuml/sdnode/.cd, dt/.get=\tikzumlSDNodeDT, name/.get=\tikzumlSDNodeName}% + % + \ifthenelse{\equal{\tikzumlSDNodeName}{tikzumlEmpty}}{% + \expandafter\def\expandafter\tikzumlSDNode@nodeName{sdnode-\thetikzumlSDNodeNum}% + }{% + \expandafter\def\expandafter\tikzumlSDNode@nodeName{\tikzumlSDNodeName}% + }% + % + \stepcounter{tikzumlSDNodeNum}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlSDNode@objnodeName{#2}}\x% + % + \draw (\expandafter\csname tikzumlLastChild@\tikzumlSDNode@objnodeName \endcsname)+(0,-\tikzumlSDNodeDT ex) node[name=\tikzumlSDNode@nodeName,coordinate] {};% + % + % update last node drawn on sender lifeline + \expandafter\xdef\csname tikzumlLastChild@\tikzumlSDNode@objnodeName \endcsname{\tikzumlSDNode@nodeName}% +}% +% +\newlength{\tikzumlCall@xa}% +\newlength{\tikzumlCall@xb}% +% +% define a uml operation call for sequence diagrams +% args : call sender +% call receiver +% optional : dt: time delay from precedent event end +% name: name of the call +% op: operation name and input args +% return: return value +% type: type of the call (synchron, asynchron) +% draw, fill, text: colors +% padding: time padding from call start and to call end +\newenvironment{umlcall}[3][]{% + \stepcounter{tikzumlCallNum}% + \def\tikzumlCallWithReturn{tikzumlFalse}% + \edef\tikzumlCall@lastchildNum{\thetikzumlCallNum}% for testing presence of sub-calls + \gdef\tikzumlCallBottom{0}% + % + \pgfkeys{/tikzuml/call/.cd, dt/.initial=\tikzumlCallDefaultDT, name/.initial={call-\thetikzumlCallNum},% + op/.initial={}, return/.initial={}, type/.initial=\tikzumlCallDefaultType,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlCallDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + padding/.initial=\tikzumlCallDefaultPadding,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{with return}}{% + \def\tikzumlCallWithReturn{tikzumlTrue}% + }{% + \errmessage{TIKZUML ERROR : in umlcall, invalid option \keyname}% + }% + }% + }% + \pgfkeys{/tikzuml/call/.cd, #1}% + \pgfkeys{/tikzuml/call/.cd, dt/.get=\tikzumlCallDT, name/.get=\tikzumlCallName, op/.get=\tikzumlCallOp,% + return/.get=\tikzumlCallReturn, type/.get=\tikzumlCallType,% + padding/.get=\tikzumlCallPadding,% + draw/.get=\tikzumlCallDrawColor, fill/.get=\tikzumlCallFillColor,% + text/.get=\tikzumlCallTextColor% + }% + % + \edef\tikzumlfillcall{\tikzumlCallFillColor}% + \edef\tikzumldrawcall{\tikzumlCallDrawColor}% + \edef\tikzumltextcall{\tikzumlCallTextColor}% + \edef\tikzumltypecall{\tikzumlCallType}% + % + \ifthenelse{\equal{\tikzumlCallDT}{tikzumlEmpty}}{% + \ifnum\thetikzumlCallNum=1% + \def\tikzumlCallDT{2}% + \def\tikzumlcallSrc{2}% + \else% + \def\tikzumlCallDT{2}% + \def\tikzumlcallSrc{1}% + \fi% + }{% + \def\tikzumlcallSrc{0}% + }% + % + \let\tikzumlCallStartNodeNameold\tikzumlCallStartNodeName% + \def\tikzumlCallStartNodeName{#2}% + \let\tikzumlCallEndNodeNameold\tikzumlCallEndNodeName% + \def\tikzumlCallEndNodeName{#3}% + \def\tikzumlcallheight{\tikzumlCallPadding}% + % + % managing time delays from previous/parent fragments + \ifnum\thetikzumlCallStartFragmentNum>0% + \let\tikzumlCallDTold\tikzumlCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCallDTold}% + \edef\tikzumlCallDT{\pgfmathresult}% + \addtocounter{tikzumlCallStartFragmentNum}{-1} + \fi% + \ifnum\thetikzumlCallEndFragmentNum>0% + \let\tikzumlCallDTold\tikzumlCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCallDTold}% + \edef\tikzumlCallDT{\pgfmathresult}% + \addtocounter{tikzumlCallEndFragmentNum}{-1} + \fi% + \ifnum\thetikzumlFragmentPartNum>0% + \let\tikzumlCallDTold\tikzumlCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCallDTold}% + \edef\tikzumlCallDT{\pgfmathresult}% + \fi% + % + % managing parent-child structure + \ifnum\thetikzumlCallLevel>0% + \let\tikzumlCall@nameold\tikzumlCall@name% + \edef\tikzumlCall@name{\tikzumlCallName}% + \let\tikzumlCall@parentold\tikzumlCall@parent% + \edef\tikzumlCall@parent{\tikzumlCall@parentold @@\tikzumlCall@nameold}% + \else% + \edef\tikzumlCall@parent{}% + \edef\tikzumlCall@parentold{}% + \edef\tikzumlCall@nameold{} + \edef\tikzumlCall@name{\tikzumlCallName}% + \fi% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlCall@nodeName{\tikzumlCall@name}}\x% + % + \let\tikzumlCall@nodeNameold\tikzumlCall@nodeName% + % + \def\tikzumlcallstyle{tikzuml \tikzumlCallType-msg style}% + % + % top node of activity period of call sender + \begin{pgfonlayer}{connections}% + \pgfmathparse{\tikzumlCallDT+\tikzumlcallSrc}% + \draw (\csname tikzumlLastChild@\tikzumlCallStartNodeName \endcsname)+(0,-\pgfmathresult ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (st-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- tikzumlTmpNode) {};% + % + % update last node drawn on sender lifeline + \expandafter\xdef\csname tikzumlLastChild@\tikzumlCallStartNodeName \endcsname{st-\tikzumlCall@nodeName}% + % + % top node of activity period of call receiver + \ifthenelse{\equal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeName}}{% + \draw (st-\tikzumlCall@nodeName)+(0,-0.75*\tikzumlCallPadding ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (et-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- tikzumlTmpNode) {};% + }{% + \node[tikzuml activity style] (et-\tikzumlCall@nodeName) at (\tikzumlCallEndNodeName |- st-\tikzumlCall@nodeName) {};% + }% + % + % update last node drawn on receiver lifeline + \expandafter\xdef\csname tikzumlLastChild@\tikzumlCallEndNodeName \endcsname{et-\tikzumlCall@nodeName}% + \xdef\tikzumlCallBottomSrc{et-\tikzumlCall@nodeName}% + \end{pgfonlayer}% + % + \stepcounter{tikzumlCallLevel}% +}{% + \addtocounter{tikzumlCallLevel}{-1}% + % + % bottom nodes of activity periods of call sender and receiver + \begin{pgfonlayer}{connections}% + \ifnum\tikzumlCall@lastchildNum=\thetikzumlCallNum% + % + % this test occurs a bug with latex package preview + \ifthenelse{\equal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeName}}{% + \draw (\tikzumlCallBottomSrc)+(0,-\tikzumlCallPadding ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (eb-\tikzumlCall@nodeName) at (\tikzumlCallEndNodeName |- tikzumlTmpNode) {};% + \draw (eb-\tikzumlCall@nodeName)+(0,-0.75*\tikzumlCallPadding ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (sb-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- tikzumlTmpNode) {};% + }{% + \ifthenelse{\equal{\tikzumlCallReturn}{tikzumlEmpty}}{% + \pgfmathsetmacro{\tikzumlCallPaddingd}{0.5*\tikzumlCallPadding}% + }{% + \pgfmathsetmacro{\tikzumlCallPaddingd}{1.2*\tikzumlCallPadding}% + }% + \draw (\tikzumlCallBottomSrc)+(0,-\tikzumlCallPaddingd ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (eb-\tikzumlCall@nodeName) at (\tikzumlCallEndNodeName |- tikzumlTmpNode) {};% + \node[tikzuml activity style] (sb-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- eb-\tikzumlCall@nodeName) {};% + }% + \xdef\tikzumlCallBottomSrc{sb-\tikzumlCall@nodeName}% + \else% + % + % managing time delays from previous/parent fragments + \ifnum\thetikzumlCallStartFragmentNum>0% + \let\tikzumlcallheightold\tikzumlCallPadding% + \pgfmathparse{\tikzumlcallheightold+0.5*\tikzumlFragment@paddingy}% + \edef\tikzumlcallheight{\pgfmathresult}% + \addtocounter{tikzumlCallStartFragmentNum}{-1}% + \fi% + \ifnum\thetikzumlCallEndFragmentNum>0% + \let\tikzumlcallheightold\tikzumlCallPadding% + \pgfmathparse{\tikzumlcallheightold+0.5*\tikzumlFragment@paddingy}% + \edef\tikzumlcallheight{\pgfmathresult}% + \addtocounter{tikzumlCallEndFragmentNum}{-1}% + \fi% + % + \ifthenelse{\equal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeName}}{% + \draw (\tikzumlCallBottomSrc)+(0,-\tikzumlcallheight ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (eb-\tikzumlCall@nodeName) at (\tikzumlCallEndNodeName |- tikzumlTmpNode) {};% + \draw (eb-\tikzumlCall@nodeName)+(0,-0.75*\tikzumlCallPadding ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (sb-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- tikzumlTmpNode) {};% + }{% + \draw (\tikzumlCallBottomSrc)+(0,-\tikzumlcallheight ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (eb-\tikzumlCall@nodeName) at (\tikzumlCallEndNodeName |- tikzumlTmpNode) {};% + \node[tikzuml activity style] (sb-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- eb-\tikzumlCall@nodeName) {};% + }% + % + \xdef\tikzumlCallBottomSrc{sb-\tikzumlCall@nodeName}% + \fi% + \end{pgfonlayer}% + % + % draw activity periods + \begin{pgfonlayer}{activity}% + \ifthenelse{\equal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeName}}{% + % draw root activity period only + \ifnum\thetikzumlCallLevel=0% + \draw[draw=\tikzumldrawcall, fill=\tikzumlfillcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south east);% + \else% + % draw root activity from inner call + \ifthenelse{\equal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeNameold}}{}{% + \draw[draw=\tikzumldrawcall, fill=\tikzumlfillcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south east);% + }% + \fi% + }{% + % draw root activity period + \ifnum\thetikzumlCallLevel=0% + \draw[draw=\tikzumldrawcall, fill=\tikzumlfillcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south east);% + \else% + % draw root activity from inner call + \ifthenelse{\equal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeNameold}}{}{% + \draw[draw=\tikzumldrawcall, fill=\tikzumlfillcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south east);% + }% + \fi% + % draw receiver activity period + \draw[draw=\tikzumldrawcall, fill=\tikzumlfillcall] (et-\tikzumlCall@nodeName.north west) rectangle (eb-\tikzumlCall@nodeName.south east);% + }% + \end{pgfonlayer}% + \ifthenelse{\equal{\tikzumlCallDefaultFillColor}{\tikzumlCallFillColor}}{}{% + \fill[\tikzumlfillcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south east);% + \draw[\tikzumldrawcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south west) (st-\tikzumlCall@nodeName.north east) rectangle (sb-\tikzumlCall@nodeName.south east);% + }% + % + % update last nodes drawn on sender and receiver lifelines + \expandafter\xdef\csname tikzumlLastChild@\tikzumlCallEndNodeName \endcsname{eb-\tikzumlCall@nodeName}% + \expandafter\xdef\csname tikzumlLastChild@\tikzumlCallStartNodeName \endcsname{sb-\tikzumlCall@nodeName}% + % + % draw call arrows + \begin{pgfonlayer}{connections}% + \ifthenelse{\equal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeName}}{% + \draw[\tikzumlcallstyle, \tikzumldrawcall] (st-\tikzumlCall@nodeName.east) -- ++(2.5*\tikzumlCallPadding ex,0) % + -- ++(0,-0.75*\tikzumlCallPadding ex) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, right, name=\tikzumlCall@nodeName-op] {\tikzumlCallOp} % + -- (et-\tikzumlCall@nodeName.east);% + % + % draw return arrow and update fit for parent fragment + \ifthenelse{\equal{\tikzumltypecall}{synchron}}{% + \ifthenelse{\NOT\equal{\tikzumlCallReturn}{}\OR\equal{\tikzumlCallWithReturn}{tikzumlTrue}}{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op) (\tikzumlCall@nodeName-return)}% + \fi% + % + \draw[tikzuml call return style, \tikzumldrawcall] (eb-\tikzumlCall@nodeName.east) -- ++(2.5*\tikzumlCallPadding ex,0) + -- ++(0,-0.75*\tikzumlCallPadding ex) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, right, name=\tikzumlCall@nodeName-return] {\tikzumlCallReturn} % + -- (sb-\tikzumlCall@nodeName.east);% + }{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op)}% + \fi% + }% + }{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op)}% + \fi% + }% + }{% + % draw call arrows + \pgfextractx{\tikzumlCall@xa}{\pgfpointanchor{\tikzumlCallStartNodeName}{center}}% + \pgfextractx{\tikzumlCall@xb}{\pgfpointanchor{\tikzumlCallEndNodeName}{center}}% + % + \ifthenelse{\tikzumlCall@xb>\tikzumlCall@xa}{% + \draw[\tikzumlcallstyle, \tikzumldrawcall] (st-\tikzumlCall@nodeName.east) -- (et-\tikzumlCall@nodeName.west) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, above=-0.4ex, name=\tikzumlCall@nodeName-op] {\tikzumlCallOp};% + }{% + \draw[\tikzumlcallstyle, \tikzumldrawcall] (st-\tikzumlCall@nodeName.west) -- (et-\tikzumlCall@nodeName.east) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, above=-0.4ex, name=\tikzumlCall@nodeName-op] {\tikzumlCallOp};% + }% + % + % draw return arrow and update fit for parent fragment + \ifthenelse{\equal{\tikzumltypecall}{synchron}}{% + \ifthenelse{\NOT\equal{\tikzumlCallReturn}{}\OR\equal{\tikzumlCallWithReturn}{tikzumlTrue}}{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op) (\tikzumlCall@nodeName-return)}% + \fi% + % + \ifthenelse{\tikzumlCall@xb>\tikzumlCall@xa}{% + \draw[tikzuml call return style, \tikzumldrawcall] (eb-\tikzumlCall@nodeName.west) -- (sb-\tikzumlCall@nodeName.east) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, above=-0.4ex, name=\tikzumlCall@nodeName-return] {\tikzumlCallReturn};% + }{% + \draw[tikzuml call return style, \tikzumldrawcall] (eb-\tikzumlCall@nodeName.east) -- (sb-\tikzumlCall@nodeName.west) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, above=-0.4ex, name=\tikzumlCall@nodeName-return] {\tikzumlCallReturn};% + }% + }{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op)}% + \fi% + }% + }{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op)}% + \fi% + }% + }% + \end{pgfonlayer}% +}% +% +% alias for function self call +\newenvironment{umlcallself}[2][]{\begin{umlcall}[#1]{#2}{#2} }{\end{umlcall}}% +% +% define a combined fragment +% optional : name: name of fragment +% type: type of fragment (opt, alt, break, loop, par, critical, ignore, consider, assert, neg, weak, strict, ref) +% label: label of fragment (ex : condition for opt, iterator for loop, ...) +% inner xsep, inner ysep: padding of the fragment box +% draw, fill, text: colors +\newenvironment{umlfragment}[1][]{% + % define a fragment separator + % optional : label of the fragment part (ex : else for alt) + \providecommand{\umlfpart}[1][]{% + \stepcounter{tikzumlFragmentPartNum}% + % + \node[outer sep=0, inner xsep=\tikzumlFragmentXSep ex, inner ysep=\tikzumlFragmentYSep ex, fit=\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname, name=\tikzumlFragment@name-Part-tmp] {};% + \node[anchor=east, name=\tikzumlFragment@name-PartType-\thetikzumlFragmentPartNum] at (\tikzumlFragment@name-Part-tmp.north west |- \tikzumlCallBottomSrc) {\phantom{\tikzumlFragmentType}};% + \draw (\tikzumlFragment@name-PartType-\thetikzumlFragmentPartNum.north west |- \tikzumlCallBottomSrc)+(0,-0.4*\tikzumlFragment@paddingy ex) node[name=\tikzumlFragment@name-PartWest-\thetikzumlFragmentPartNum] {};% + \draw (\tikzumlFragment@name-Part-tmp.north east |- \tikzumlCallBottomSrc)+(0,-0.4*\tikzumlFragment@paddingy ex) node[name=\tikzumlFragment@name-PartEast-\thetikzumlFragmentPartNum] {};% + \draw[dashed] (\tikzumlFragment@name-PartWest-\thetikzumlFragmentPartNum) -- (\tikzumlFragment@name-PartEast-\thetikzumlFragmentPartNum); + \draw (\tikzumlFragment@name-PartType-\thetikzumlFragmentPartNum)+(0,-0.4*\tikzumlFragment@paddingy ex) node[name=tikzumlTmpNode] {\phantom{\tikzumlFragmentType}};% + \node[anchor=north west] at (tikzumlTmpNode.south west) {[##1]};% + }% + % + \stepcounter{tikzumlFragmentNum}% + % + \pgfkeys{/tikzuml/fragment/.cd, name/.initial=fragment@\alph{tikzumlFragmentNum}, type/.initial=\tikzumlFragmentDefaultType,% + label/.initial=tikzumlEmpty,% + inner xsep/.initial=\tikzumlFragmentDefaultXSep, inner ysep/.initial=\tikzumlFragmentDefaultYSep,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlFragmentDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlfragment, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/fragment/.cd, #1}% + \pgfkeys{/tikzuml/fragment/.cd, name/.get=\tikzumlFragmentName, type/.get=\tikzumlFragmentType,% + label/.get=\tikzumlFragmentLabel,% + inner xsep/.get=\tikzumlFragmentXSep, inner ysep/.get=\tikzumlFragmentYSep,% + draw/.get=\tikzumlFragmentDrawColor, fill/.get=\tikzumlFragmentFillColor,% + text/.get=\tikzumlFragmentTextColor% + }% + % + \ifthenelse{\equal{\tikzumlFragmentLabel}{tikzumlEmpty}}{% + \def\tikzumlFragmentLabel{}% + }{% + \let\tikzumlFragmentLabelold\tikzumlFragmentLabel% + \def\tikzumlFragmentLabel{[\tikzumlFragmentLabelold]}% + }% + % + \ifnum\thetikzumlFragmentLevel>0% + \let\tikzumlFragment@parentold\tikzumlFragment@parent% + \let\tikzumlFragment@nameold\tikzumlFragment@name% + \edef\tikzumlFragment@parent{\tikzumlFragment@nameold}% + \else% + \setcounter{tikzumlFragmentPartNum}{0}% + \edef\tikzumlFragment@parent{}% + \edef\tikzumlFragment@parentold{}% + \edef\tikzumlFragment@nameold{}% + \fi% + % + \edef\tikzumlFragment@name{\tikzumlFragmentName}% + \expandafter\gdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{}% + % + \stepcounter{tikzumlFragmentLevel}% + % + \ifnum\thetikzumlCallLevel>0% + \stepcounter{tikzumlCallStartFragmentNum}% + \fi% + % + \pgfmathparse{6*\tikzumlFragmentYSep}% + \xdef\tikzumlFragment@paddingy{\pgfmathresult}% + \if\c@tikzumlFragmentLevelNum=0% + \setcounter{tikzumlFragmentLevelNum}{\thetikzumlFragmentLevel}% + \fi% + % + % time delay adjustment for two consecutive fragments + \ifnum\thetikzumlCallEndFragmentNum>0% + \addtocounter{tikzumlCallEndFragmentNum}{-1} + \fi% +}{% + % + \addtocounter{tikzumlFragmentLevel}{-1}% + % + \ifnum\thetikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@parent \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@parent \endcsname{\tikzumlFragmentFitOld (\tikzumlFragment@name)}% + \fi% + % + % draw working fragment box + \begin{pgfonlayer}{fragment\thetikzumlFragmentLevel}% + \node[outer sep=0, inner xsep=\tikzumlFragmentXSep ex, inner ysep=\tikzumlFragmentYSep ex, fit=\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname, name=\tikzumlFragment@name-back] {};% + \end{pgfonlayer}% + % + % draw type and label + \node[text=\tikzumlFragmentTextColor, font=\tikzumlDefaultFont, anchor=north east, name=\tikzumlFragment@name-type] % + at (\tikzumlFragment@name-back.north west) {\tikzumlFragmentType};% + \node[text=\tikzumlFragmentTextColor, font=\tikzumlDefaultFont, anchor=north west, name=\tikzumlFragment@name-label] % + at (\tikzumlFragment@name-type.south west) {\tikzumlFragmentLabel};% + % + % draw final fragment box + \begin{pgfonlayer}{fragment\thetikzumlFragmentLevel}% + \node[draw=\tikzumlFragmentDrawColor, fill=\tikzumlFragmentFillColor, outer sep=0, inner sep=0, font=\tikzumlDefaultFont, fit=(\tikzumlFragment@name-back) (\tikzumlFragment@name-type) (\tikzumlFragment@name-label), name=\tikzumlFragment@name] {};% + \end{pgfonlayer}% + % + \draw[draw=\tikzumlFragmentDrawColor] (\tikzumlFragment@name.north west) rectangle (\tikzumlFragment@name.south east);% + \draw (\tikzumlFragment@name-type.south east)+(0,1ex) node[name=\tikzumlFragment@name-typetop, inner sep=0] {};% + \draw (\tikzumlFragment@name-type.south east)+(-1ex,0) node[name=\tikzumlFragment@name-typeleft, inner sep=0] {};% + \draw (\tikzumlFragment@name.north west) -| (\tikzumlFragment@name-typetop.center) -- (\tikzumlFragment@name-typeleft.center) -| (\tikzumlFragment@name.north west);% + % + \ifnum\thetikzumlCallLevel>0% + \stepcounter{tikzumlCallEndFragmentNum}% + \fi% +}% +% +% define a constructor call +% arg : call sender +% name of constructed object +% optional : x: coordinate of the new object +% stereo: stereotype of the new object +% class: class type of the new object +% dt: time delay from last event +% name: name of the call +% draw, fill, text: colors +% no ddots: when used, disable printing of double dots +\newcommand{\umlcreatecall}[3][]{% + \stepcounter{tikzumlCallNum}% + \edef\tikzumlCall@lastchildNum{\thetikzumlCallNum}% for testing presence of sub-calls + \gdef\tikzumlInCreateCall{1}% + \pgfkeys{/tikzuml/createcall/.cd, x/.initial=tikzumlEmpty, stereo/.initial=\tikzumlObjectDefaultStereo, class/.initial={},% + dt/.initial=\tikzumlCreateCallDefaultDT, name/.initial=call-\thetikzumlCallNum,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlCallDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + draw obj/.initial=\tikzumlDefaultDrawColor, fill obj/.initial=\tikzumlObjectDefaultFillColor,% + text obj/.initial=\tikzumlDefaultTextColor,% + no ddots/.is if=tikzumlcreatecallNoDDots,% + no ddots=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlcreatecall, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/createcall/.cd, #1}% + \pgfkeys{/tikzuml/createcall/.cd, x/.get=\tikzumlCreateCallX, stereo/.get=\tikzumlCreateCallStereo,% + class/.get=\tikzumlCreateCallClass,% + dt/.get=\tikzumlCreateCallDT, name/.get=\tikzumlCreateCallName,% + draw/.get=\tikzumlCreateCallDrawColor, fill/.get=\tikzumlCreateCallFillColor,% + text/.get=\tikzumlCreateCallTextColor,% + draw obj/.get=\tikzumlCreateCallObjectDrawColor, fill obj/.get=\tikzumlCreateCallObjectFillColor,% + text obj/.get=\tikzumlCreateCallObjectTextColor% + }% + % + \def\tikzumlCreateCallSrc@name{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlCreateCallSrc@nodeName{\tikzumlCreateCallSrc@name}}\x% + % + % managing time delays from previous/parent fragments + \ifnum\thetikzumlCallStartFragmentNum>0% + \let\tikzumlCreateCallDTold\tikzumlCreateCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCreateCallDTold}% + \edef\tikzumlCreateCallDT{\pgfmathresult}% + \addtocounter{tikzumlCallStartFragmentNum}{-1} + \fi% + \ifnum\thetikzumlCallEndFragmentNum>0% + \let\tikzumlCreateCallDTold\tikzumlCreateCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCreateCallDTold}% + \edef\tikzumlCreateCallDT{\pgfmathresult}% + \addtocounter{tikzumlCallEndFragmentNum}{-1} + \fi% + \ifnum\thetikzumlFragmentPartNum>0% + \let\tikzumlCreateCallDTold\tikzumlCreateCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCreateCallDTold}% + \edef\tikzumlCreateCallDT{\pgfmathresult}% + \fi% + % + % managing parent-child structure + \ifnum\thetikzumlCallLevel>0% + \let\tikzumlCall@nameold\tikzumlCall@name% + \def\tikzumlCall@name{\tikzumlCreateCallName}% + \let\tikzumlCall@parentold\tikzumlCall@parent% + \edef\tikzumlCall@parent{\tikzumlCall@parentold @@\tikzumlCall@nameold}% + \else% + \edef\tikzumlCall@parent{}% + \edef\tikzumlCall@parentold{}% + \edef\tikzumlCall@nameold{} + \edef\tikzumlCall@name{\tikzumlCreateCallName}% + \fi% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlCreateCall@nodeName{\tikzumlCall@name}}\x% + % + \draw (\csname tikzumlLastChild@\tikzumlCreateCallSrc@nodeName \endcsname)+(0,-\tikzumlCreateCallDT ex) node[name=st-\tikzumlCreateCall@nodeName, tikzuml activity style] {};% + % + \xdef\tikzumlCreateCallObjectSrc{st-\tikzumlCreateCall@nodeName}% + % + \iftikzumlcreatecallNoDDots% + \umlobject[x=\tikzumlCreateCallX, stereo=\tikzumlCreateCallStereo, class=\tikzumlCreateCallClass, draw=\tikzumlCreateCallObjectDrawColor, fill=\tikzumlCreateCallObjectFillColor, text=\tikzumlCreateCallObjectTextColor, no ddots]{#3}% + \else + \umlobject[x=\tikzumlCreateCallX, stereo=\tikzumlCreateCallStereo, class=\tikzumlCreateCallClass, draw=\tikzumlCreateCallObjectDrawColor, fill=\tikzumlCreateCallObjectFillColor, text=\tikzumlCreateCallObjectTextColor]{#3}% + \fi + % + \draw (\csname tikzumlLastChild@\tikzumlCreateCallSrc@nodeName \endcsname |- #3)+(0,-0.5*\tikzumlCreateCallDT ex) node[name=sb-\tikzumlCreateCall@nodeName, tikzuml activity style] {};% + % + \expandafter\xdef\csname tikzumlLastChild@\tikzumlCreateCallSrc@nodeName \endcsname{sb-\tikzumlCreateCall@nodeName}% + \xdef\tikzumlCallBottomSrc{sb-\tikzumlCreateCall@nodeName}% + % + \begin{pgfonlayer}{connections}% + \draw[tikzuml synchron-msg style, \tikzumlCreateCallDrawColor] (st-\tikzumlCreateCall@nodeName) -- (#3) node[midway, above, font=\tikzumlDefaultFont, text=\tikzumlCreateCallTextColor, name=\tikzumlCreateCall@nodeName-op] {create};% + \end{pgfonlayer}% + % + \ifnum\thetikzumlCallLevel=0% + \begin{pgfonlayer}{activity}% + \draw[draw=\tikzumlCreateCallDrawColor, fill=\tikzumlCreateCallFillColor] (st-\tikzumlCreateCall@nodeName.north west) rectangle (sb-\tikzumlCreateCall@nodeName.south east);% + \end{pgfonlayer}% + \fi% + % add to fit fragment + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCreateCall@nodeName) (sb-\tikzumlCreateCall@nodeName) (\tikzumlCreateCall@nodeName-op) (#3) }% + \fi% +} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% component diagrams % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\tikzstyle{tikzuml connector style}=[color=\tikzumlDefaultDrawColor, -]% +% +\newcounter{tikzumlComponentLevel}% +\newcounter{tikzumlComponentSubComponentNum}% +\newcounter{tikzumlConnectorNum}% +\setcounter{tikzumlConnectorNum}{1}% +% +\newif\iftikzumlcomponentWithoutCoords% +% +\newcommand{\picturedcomponent}[1]{% + \pgfkeys{/tikzuml/component/picture/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/component/picture/.cd,#1}% + \pgfkeys{/tikzuml/component/picture/.cd, scale/.get=\tikzumlComponentScale}% + \begin{tikzpicture}[#1]% + \filldraw (0,0) rectangle (1ex,1.5ex);% + \filldraw (-0.2ex,0.4ex) rectangle (0.2ex,0.6ex);% + \filldraw (-0.2ex,0.9ex) rectangle (0.2ex,1.1ex);% + \end{tikzpicture}% +}% +% +% define a uml component +% args : name of the component +% content of the component +% optional args : x,y coordinates of the component +% width of the component node +\newenvironment{umlcomponent}[2][]{% + \ifnum\thetikzumlComponentLevel>0% + \let\tikzumlComponent@nameold\tikzumlComponent@fitname% + \let\tikzumlComponent@parentold\tikzumlComponent@parent% + \edef\tikzumlComponent@parent{\tikzumlComponent@parentold @@\tikzumlComponent@nameold}% + \else% + \def\tikzumlComponent@parent{}% + \fi% + % + \stepcounter{tikzumlComponentLevel}% + % + \pgfkeys{/tikzuml/component/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlComponentDefaultWidth, name/.initial={},% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlComponentDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor, style/.style={},% + no coords/.is if=tikzumlcomponentWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/component/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/component/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/component/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlcomponent, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/component/.cd, #1}% + \pgfkeys{/tikzuml/component/.cd, x/.get=\tikzumlComponentXShift, y/.get=\tikzumlComponentYShift,% + width/.get=\tikzumlComponentMinimumWidth, name/.get=\tikzumlComponentName,% + draw/.get=\tikzumlComponentDrawColor, fill/.get=\tikzumlComponentFillColor,% + text/.get=\tikzumlComponentTextColor% + }% + % + \ifthenelse{\equal{\tikzumlComponentName}{}}{% + \edef\tikzumlComponent@name{#2}% + }{% + \edef\tikzumlComponent@name{\tikzumlComponentName}% + }% + % + \begingroup% + \def\_{@}\edef\x{\endgroup% + \def\noexpand\tikzumlComponent@fitname{\tikzumlComponent@name}}\x% + % + \let\tikzumlComponent@nodeNameold\tikzumlComponent@nodeName% + \def\tikzumlComponent@caption{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlComponent@nodeName{\tikzumlComponent@name}}\x% + % + \expandafter\gdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{}% + % + \setcounter{tikzumlComponentSubComponentNum}{0}% + % + \begin{scope}[xshift=\tikzumlComponentXShift cm, yshift=\tikzumlComponentYShift cm]% +}{% + \addtocounter{tikzumlComponentLevel}{-1}% + \begin{pgfonlayer}{component\thetikzumlComponentLevel}% + % + % if contains nothing, one define a fictive node to enable the fit option + \ifnum\c@tikzumlComponentSubComponentNum=0% + \iftikzumlcomponentWithoutCoords% + \node[inner ysep=0.5ex, minimum width=\tikzumlComponentMinimumWidth, font=\tikzumlDefaultFont, /tikzuml/component/style] (\tikzumlComponent@nodeName-root) {\phantom{\tikzumlComponent@nodeName}};% + \else% + \node[inner ysep=0.5ex, minimum width=\tikzumlComponentMinimumWidth, font=\tikzumlDefaultFont, /tikzuml/component/style] (\tikzumlComponent@nodeName-root) at (0,0) {\phantom{\tikzumlComponent@nodeName}};% + \fi% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{(\tikzumlComponent@nodeName-root)}% + \fi% + % + \ifnum\c@tikzumlComponentLevel>0% + \def\tikzumlComponentFitTmp{\csname tikzumlComponentFit\tikzumlComponent@parent\endcsname}% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent\endcsname{\tikzumlComponentFitTmp (\tikzumlComponent@nodeName-body) (\tikzumlComponent@nodeName-caption)}% + \stepcounter{tikzumlComponentSubComponentNum}% + \fi% + % + \node[inner sep=2ex, font=\tikzumlDefaultFont, fit = \csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname] (\tikzumlComponent@nodeName-body) {};% + \node[above, font=\tikzumlDefaultFont] (\tikzumlComponent@nodeName-captiontmp) at (\tikzumlComponent@nodeName-body.north) {\tikzumlComponent@caption};% + \node (\tikzumlComponent@nodeName-logotmp) at (\tikzumlComponent@nodeName-captiontmp.north -| \tikzumlComponent@nodeName-body.east) {\picturedcomponent{draw=\tikzumlComponentDrawColor, fill=\tikzumlComponentFillColor, font=\tikzumlDefaultFont} };% + \node[draw=\tikzumlComponentDrawColor, fill=\tikzumlComponentFillColor, name=\tikzumlComponent@nodeName, /tikzuml/component/style, fit=(\tikzumlComponent@nodeName-body) (\tikzumlComponent@nodeName-captiontmp)] {};% + \node[font=\tikzumlDefaultFont] (\tikzumlComponent@nodeName-caption) at (\tikzumlComponent@nodeName-captiontmp) {\tikzumlComponent@caption};% + \draw (\tikzumlComponent@nodeName-caption.north -| \tikzumlComponent@nodeName.east) node[font=\tikzumlDefaultFont, xshift=-1ex, below=-1ex, name=\tikzumlComponent@nodeName-logo] {\picturedcomponent{draw=\tikzumlComponentDrawColor, fill=\tikzumlComponentFillColor, font=\tikzumlDefaultFont} };% + \draw (\tikzumlComponent@nodeName-caption.south -| \tikzumlComponent@nodeName.north west) -- (\tikzumlComponent@nodeName-caption.south -| \tikzumlComponent@nodeName.north east);% + \coordinate (\tikzumlComponent@nodeName-west-port) at (\tikzumlComponent@nodeName.west); + \coordinate (\tikzumlComponent@nodeName-east-port) at (\tikzumlComponent@nodeName.east); + \coordinate (\tikzumlComponent@nodeName-south-port) at (\tikzumlComponent@nodeName.south); + \coordinate (\tikzumlComponent@nodeName-north-port) at (\tikzumlComponent@nodeName.north); + \end{pgfonlayer}% + \end{scope}% + % + % add to fit + \ifnum\c@tikzumlPackageLevel>0% + \edef\tikzumlPackageFitOld{\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname}% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlComponent@nodeName)}% + \stepcounter{tikzumlPackageClassNum}% + \fi% +}% +% +% shortcut for empty component +\newcommand{\umlbasiccomponent}[2][]{\begin{umlcomponent}[#1]{#2} \end{umlcomponent}}% +% +\newcommand{\umlrequiredinterface}[2][]{% + \def\tikzumlInterfaceWithPort{tikzumlFalse}% + \pgfkeys{/tikzuml/requiredinterfacerelation/.cd, interface/.initial={}, distance/.initial=\tikzumlRequiredInterfaceDefaultDistance,% + name/.initial=tikzumlEmpty, width/.initial=\tikzumlRequiredInterfaceDefaultWidth,% + padding/.initial=\tikzumlRequiredInterfaceDefaultPadding,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlComponentDefaultFillColor,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{with port}}{% + \def\tikzumlInterfaceWithPort{tikzumlTrue}% + }{}% + }% + }% + \pgfkeys{/tikzuml/requiredinterfacerelation/.cd, #1}% + \pgfkeys{/tikzuml/requiredinterfacerelation/.cd, interface/.get=\tikzumlRequiredInterfaceLabel,% + distance/.get=\tikzumlRequiredInterfaceDistance,% + name/.get=\tikzumlRequiredInterfaceName,% + width/.get=\tikzumlRequiredInterfaceWidth,% + padding/.get=\tikzumlRequiredInterfacePadding,% + draw/.get=\tikzumlRequiredInterfaceDrawColor,% + fill/.get=\tikzumlRequiredInterfaceFillColor% + }% + % + \ifthenelse{\equal{\tikzumlRequiredInterfaceName}{tikzumlEmpty}}{% + \edef\tikzumlRequiredInterface@interfacename{#2-east-interface}% + \edef\tikzumlRequiredInterface@portname{#2-east-port}% + \edef\tikzumlRequiredInterface@paddingname{#2-east-padding}% + }{% + \edef\tikzumlRequiredInterface@interfacename{\tikzumlRequiredInterfaceName}% + \edef\tikzumlRequiredInterface@portname{\tikzumlRequiredInterfaceName-port}% + \edef\tikzumlRequiredInterface@paddingname{\tikzumlRequiredInterfaceName-padding}% + }% + % + \edef\tikzumlRequiredInterface@name{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlRequiredInterface@nodeName{\tikzumlRequiredInterface@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlRequiredInterface@interfacenodeName{\tikzumlRequiredInterface@interfacename}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlRequiredInterface@portnodeName{\tikzumlRequiredInterface@portname}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlRequiredInterface@paddingnodeName{\tikzumlRequiredInterface@paddingname}}\x% + % + \ifthenelse{\equal{\tikzumlInterfaceWithPort}{tikzumlTrue}}{% + \node[inner sep=0.5*\tikzumlRequiredInterfaceWidth, rectangle, draw=\tikzumlRequiredInterfaceDrawColor, fill=\tikzumlRequiredInterfaceFillColor] (\tikzumlRequiredInterface@portnodeName) at (\tikzumlRequiredInterface@nodeName.east) {};% + }{% + \node[inner sep=0] (\tikzumlRequiredInterface@nodeName-east-port) at (\tikzumlRequiredInterface@nodeName.east) {};% + }% + \begin{scope}% + \draw (\tikzumlRequiredInterface@nodeName)+(\tikzumlRequiredInterfaceDistance,0) node[inner sep=0, text width=\tikzumlRequiredInterfaceWidth, circle, name=\tikzumlRequiredInterface@interfacenodeName-tmp] {};% + \clip (\tikzumlRequiredInterface@interfacenodeName-tmp.north) rectangle (\tikzumlRequiredInterface@interfacenodeName-tmp.south -| \tikzumlRequiredInterface@interfacenodeName-tmp.west);% + \node[inner sep=0, text width=\tikzumlRequiredInterfaceWidth, circle, draw=\tikzumlRequiredInterfaceDrawColor] (\tikzumlRequiredInterface@interfacenodeName) at (\tikzumlRequiredInterface@interfacenodeName-tmp) {};% + \end{scope}% + \node[above] at (\tikzumlRequiredInterface@interfacenodeName.north) {\tikzumlRequiredInterfaceLabel};% + % + \umlrelation[style={tikzuml connector style}, #1]{\tikzumlRequiredInterface@portnodeName}{\tikzumlRequiredInterface@interfacenodeName}% + % + \draw (\tikzumlRequiredInterface@interfacenodeName)+(\tikzumlRequiredInterfacePadding,0) node[name=\tikzumlRequiredInterface@paddingnodeName] {};% + % + % add to fit + \ifnum\c@tikzumlComponentLevel>0% + \def\tikzumlComponentFitTmp{\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname}% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{\tikzumlComponentFitTmp (\tikzumlRequiredInterface@paddingnodeName) (\tikzumlRequiredInterface@portnodeName) }% + \fi% +}% +% +\newcommand{\umlprovidedinterface}[2][]{% + \def\tikzumlInterfaceWithPort{tikzumlFalse}% + \pgfkeys{/tikzuml/providedinterfacerelation/.cd, interface/.initial={}, distance/.initial=\tikzumlProvidedInterfaceDefaultDistance,% + name/.initial=tikzumlEmpty, width/.initial=\tikzumlProvidedInterfaceDefaultWidth,% + padding/.initial=\tikzumlProvidedInterfaceDefaultPadding,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlComponentDefaultFillColor,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{with port}}{% + \def\tikzumlInterfaceWithPort{tikzumlTrue}% + }{}% + }% + }% + \pgfkeys{/tikzuml/providedinterfacerelation/.cd, #1}% + \pgfkeys{/tikzuml/providedinterfacerelation/.cd, interface/.get=\tikzumlProvidedInterfaceLabel,% + distance/.get=\tikzumlProvidedInterfaceDistance,% + name/.get=\tikzumlProvidedInterfaceName,% + width/.get=\tikzumlProvidedInterfaceWidth,% + padding/.get=\tikzumlProvidedInterfacePadding,% + draw/.get=\tikzumlProvidedInterfaceDrawColor,% + fill/.get=\tikzumlProvidedInterfaceFillColor% + }% + % + \ifthenelse{\equal{\tikzumlProvidedInterfaceName}{tikzumlEmpty}}{% + \edef\tikzumlProvidedInterface@interfacename{#2-west-interface}% + \edef\tikzumlProvidedInterface@portname{#2-west-port}% + \edef\tikzumlProvidedInterface@paddingname{#2-west-padding}% + }{% + \edef\tikzumlProvidedInterface@interfacename{\tikzumlProvidedInterfaceName}% + \edef\tikzumlProvidedInterface@portname{\tikzumlProvidedInterfaceName-port}% + \edef\tikzumlProvidedInterface@paddingname{\tikzumlProvidedInterfaceName-padding}% + }% + % + \edef\tikzumlProvidedInterface@name{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlProvidedInterface@nodeName{\tikzumlProvidedInterface@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlProvidedInterface@interfacenodeName{\tikzumlProvidedInterface@interfacename}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlProvidedInterface@portnodeName{\tikzumlProvidedInterface@portname}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlProvidedInterface@paddingnodeName{\tikzumlProvidedInterface@paddingname}}\x% + % + \ifthenelse{\equal{\tikzumlInterfaceWithPort}{tikzumlTrue}}{% + \node[inner sep=0.5*\tikzumlProvidedInterfaceWidth, rectangle, draw=\tikzumlProvidedInterfaceDrawColor, fill=\tikzumlProvidedInterfaceFillColor] (\tikzumlProvidedInterface@portnodeName) at (\tikzumlProvidedInterface@nodeName.west) {};% + }{% + \node[inner sep=0] (\tikzumlProvidedInterface@portnodeName) at (\tikzumlProvidedInterface@nodeName.west) {};% + }% + \draw (\tikzumlProvidedInterface@nodeName)+(-\tikzumlProvidedInterfaceDistance,0) node[inner sep=0, text width=\tikzumlProvidedInterfaceWidth, circle, draw=\tikzumlProvidedInterfaceDrawColor, fill=\tikzumlProvidedInterfaceFillColor, name=\tikzumlProvidedInterface@interfacenodeName] {};% + \node[above] at (\tikzumlProvidedInterface@interfacenodeName.north) + {\tikzumlProvidedInterfaceLabel};% + % + \umlrelation[style={tikzuml connector style}, #1]{\tikzumlProvidedInterface@portnodeName}{\tikzumlProvidedInterface@interfacenodeName}% + % + \draw (\tikzumlProvidedInterface@interfacenodeName)+(-\tikzumlProvidedInterfacePadding,0) node[name=\tikzumlProvidedInterface@paddingnodeName] {};% + % + % add to fit + \ifnum\thetikzumlComponentLevel>0% + \def\tikzumlComponentFitTmp{\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname}% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{\tikzumlComponentFitTmp (\tikzumlProvidedInterface@paddingnodeName) (\tikzumlProvidedInterface@portnodeName) }% + \fi% +}% +% +\newlength{\tikzuml@AC@xa}% +\newlength{\tikzuml@AC@ya}% +\newlength{\tikzuml@AC@xb}% +\newlength{\tikzuml@AC@yb}% +\newlength{\tikzuml@AC@xi}% +\newlength{\tikzuml@AC@yi}% +\newlength{\tikzuml@AC@xic}% +\newlength{\tikzuml@AC@yic}% +\newlength{\tikzuml@AC@xio}% +\newlength{\tikzuml@AC@yio}% +\newlength{\tikzuml@AC@AB}% +\newlength{\tikzuml@AC@lambda}% +\newlength{\tikzuml@AC@xtrc}% +\newlength{\tikzuml@AC@ytrc}% +\newlength{\tikzuml@AC@xtlc}% +\newlength{\tikzuml@AC@ytlc}% +\newlength{\tikzuml@AC@xblc}% +\newlength{\tikzuml@AC@yblc}% +\newlength{\tikzuml@AC@xbrc}% +\newlength{\tikzuml@AC@ybrc}% +\newlength{\tikzuml@AC@middleArm}% +% +\newcommand{\umlassemblyconnectorsymbol}[2]{% + \ifthenelse{\NOT\equal{\tikzumlAssemblyConnectorLabel}{}}{% + \edef\tikzuml@ACStart@name{#1}% + \edef\tikzuml@ACEnd@name{#2}% + \edef\tikzuml@AC@width{\tikzumlAssemblyConnectorWidth}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzuml@ACStart@nodeName{\tikzuml@ACStart@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzuml@ACEnd@nodeName{\tikzuml@ACEnd@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzuml@ACInterface@nodeName{\tikzumlAssemblyConnectorSymbolName}}\x% + % + \pgfextractx{\tikzuml@AC@xa}{\pgfpointanchor{\tikzuml@ACStart@nodeName}{\tikzumlAssemblyConnectorStartAnchor}}% + \pgfextracty{\tikzuml@AC@ya}{\pgfpointanchor{\tikzuml@ACStart@nodeName}{\tikzumlAssemblyConnectorStartAnchor}}% + \pgfextractx{\tikzuml@AC@xb}{\pgfpointanchor{\tikzuml@ACEnd@nodeName}{\tikzumlAssemblyConnectorEndAnchor}}% + \pgfextracty{\tikzuml@AC@yb}{\pgfpointanchor{\tikzuml@ACEnd@nodeName}{\tikzumlAssemblyConnectorEndAnchor}}% + \pgfmathsetlength{\tikzuml@AC@xi}{0.5*\tikzuml@AC@xa+0.5*\tikzuml@AC@xb}% + \pgfmathsetlength{\tikzuml@AC@yi}{0.5*\tikzuml@AC@ya+0.5*\tikzuml@AC@yb}% + \pgfmathsetlength{\tikzuml@AC@AB}{veclen(\tikzuml@AC@xa-\tikzuml@AC@xb,\tikzuml@AC@ya-\tikzuml@AC@yb)}% + \pgfmathsetlength{\tikzuml@AC@lambda}{0.25*\tikzuml@AC@width/\tikzuml@AC@AB}% + \pgfmathsetlength{\tikzuml@AC@xic}{\tikzuml@AC@xi-\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa)}% + \pgfmathsetlength{\tikzuml@AC@yic}{\tikzuml@AC@yi-\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya)}% + \pgfmathsetlength{\tikzuml@AC@xio}{\tikzuml@AC@xi+\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa)}% + \pgfmathsetlength{\tikzuml@AC@yio}{\tikzuml@AC@yi+\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya)}% + \node[inner sep=0.5*\tikzuml@AC@width] (\tikzuml@ACInterface@nodeName-interface) at (\tikzuml@AC@xi,\tikzuml@AC@yi) {};% + \node[inner sep=0, text width=\tikzuml@AC@width, circle, draw=\tikzumlAssemblyConnectorDrawColor, fill=\tikzumlAssemblyConnectorFillColor] (\tikzuml@ACInterface@nodeName-io) at (\tikzuml@AC@xio,\tikzuml@AC@yio) {};% + \begin{scope}% + \pgfmathsetlength{\tikzuml@AC@xtrc}{\tikzuml@AC@xic-2*\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya)}% + \pgfmathsetlength{\tikzuml@AC@ytrc}{\tikzuml@AC@yic+2*\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa)}% + \pgfmathsetlength{\tikzuml@AC@xbrc}{\tikzuml@AC@xic+2*\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya)}% + \pgfmathsetlength{\tikzuml@AC@ybrc}{\tikzuml@AC@yic-2*\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa)}% + \pgfmathsetlength{\tikzuml@AC@xtlc}{\tikzuml@AC@xic-3*\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya+\tikzuml@AC@xb-\tikzuml@AC@xa)}% + \pgfmathsetlength{\tikzuml@AC@ytlc}{\tikzuml@AC@yic+3*\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa+\tikzuml@AC@ya-\tikzuml@AC@yb)}% + \pgfmathsetlength{\tikzuml@AC@xblc}{\tikzuml@AC@xic+3*\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya+\tikzuml@AC@xa-\tikzuml@AC@xb)}% + \pgfmathsetlength{\tikzuml@AC@yblc}{\tikzuml@AC@yic-3*\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa+\tikzuml@AC@yb-\tikzuml@AC@ya)}% + \coordinate (\tikzuml@ACInterface@nodeName-trc) at (\tikzuml@AC@xtrc,\tikzuml@AC@ytrc);% + \coordinate (\tikzuml@ACInterface@nodeName-brc) at (\tikzuml@AC@xbrc,\tikzuml@AC@ybrc);% + \coordinate (\tikzuml@ACInterface@nodeName-tlc) at (\tikzuml@AC@xtlc,\tikzuml@AC@ytlc);% + \coordinate (\tikzuml@ACInterface@nodeName-blc) at (\tikzuml@AC@xblc,\tikzuml@AC@yblc);% + \clip (\tikzuml@ACInterface@nodeName-trc) -- (\tikzuml@ACInterface@nodeName-tlc) -- (\tikzuml@ACInterface@nodeName-blc) -- (\tikzuml@ACInterface@nodeName-brc) -- cycle;% + \node[inner sep=0, text width=\tikzuml@AC@width, circle, draw=\tikzumlAssemblyConnectorDrawColor, fill=\tikzumlAssemblyConnectorFillColor] (\tikzuml@ACInterface@nodeName-ic) at (\tikzuml@AC@xic,\tikzuml@AC@yic) {};% + \end{scope}% + \node[above, font=\tikzumlDefaultFont] at (\tikzuml@ACInterface@nodeName-interface.north) + {\tikzumlAssemblyConnectorLabel};% + }{}% +}% +% +\newcommand{\umlassemblyconnector}[3][]{% + \def\tikzumlAssemblyConnectorWithPort{tikzumlFalse}% + \def\tikzumlAssemblyConnectorFirstArm{tikzumlFalse}% + \def\tikzumlAssemblyConnectorSecondArm{tikzumlFalse}% + \def\tikzumlAssemblyConnectorMiddleArm{tikzumlFalse}% + \def\tikzumlAssemblyConnectorLastArm{tikzumlFalse}% + \pgfkeys{/tikzuml/assemblyconnectorrelation/.cd, geometry/.initial=--, interface/.initial={},% + arm1/.initial={auto}, arm2/.initial={auto},% + name/.initial=connector-\thetikzumlConnectorNum, width/.initial=1em,% + anchor1/.initial={}, anchor2/.initial={},% + draw/.initial=\tikzumlDefaultDrawColor,% + fill assembly connector/.initial=\tikzumlAssemblyConnectorDefaultFillColor,% + fill port/.initial=\tikzumlPortDefaultFillColor,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{with port}}{% + \def\tikzumlAssemblyConnectorWithPort{tikzumlTrue}% + }{% + \ifthenelse{\equal{\keyname}{first arm}}{% + \def\tikzumlAssemblyConnectorFirstArm{tikzumlTrue}% + }{% + \ifthenelse{\equal{\keyname}{second arm}}{% + \def\tikzumlAssemblyConnectorSecondArm{tikzumlTrue}% + }{% + \ifthenelse{\equal{\keyname}{middle arm}}{% + \def\tikzumlAssemblyConnectorMiddleArm{tikzumlTrue}% + }{% + \ifthenelse{\equal{\keyname}{last arm}}{% + \def\tikzumlAssemblyConnectorLastArm{tikzumlTrue}% + }{% + }% + }% + }% + }% + }% + }% + }% + \pgfkeys{/tikzuml/assemblyconnectorrelation/.cd, #1}% + \pgfkeys{/tikzuml/assemblyconnectorrelation/.cd, geometry/.get=\tikzumlAssemblyConnectorGeometry,% + name/.get=\tikzumlAssemblyConnectorName,% + interface/.get=\tikzumlAssemblyConnectorLabel,% + width/.get=\tikzumlAssemblyConnectorWidth,% + arm1/.get=\tikzumlAssemblyConnectorStartArm,% + arm2/.get=\tikzumlAssemblyConnectorEndArm,% + anchor1/.get=\tikzumlAssemblyConnectorStartAnchorTmp,% + anchor2/.get=\tikzumlAssemblyConnectorEndAnchorTmp,% + draw/.get=\tikzumlAssemblyConnectorDrawColor,% + fill assembly connector/.get=\tikzumlAssemblyConnectorFillColor,% + fill port/.get=\tikzumlAssemblyConnectorPortFillColor% + }% + % + \edef\tikzumlAssemblyConnectorStart@name{#2}% + \edef\tikzumlAssemblyConnectorEnd@name{#3}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlAssemblyConnectorStart@nodeName{\tikzumlAssemblyConnectorStart@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlAssemblyConnectorEnd@nodeName{\tikzumlAssemblyConnectorEnd@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlAssemblyConnectorLabel@nodeName{\tikzumlAssemblyConnectorLabel}}\x% + % + \pgfextractx{\tikzuml@AC@xa}{\pgfpointanchor{\tikzumlAssemblyConnectorStart@nodeName}{center}}% + \pgfextracty{\tikzuml@AC@ya}{\pgfpointanchor{\tikzumlAssemblyConnectorStart@nodeName}{center}}% + \pgfextractx{\tikzuml@AC@xb}{\pgfpointanchor{\tikzumlAssemblyConnectorEnd@nodeName}{center}}% + \pgfextracty{\tikzuml@AC@yb}{\pgfpointanchor{\tikzumlAssemblyConnectorEnd@nodeName}{center}}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{--}}{% + \ifthenelse{\tikzuml@AC@xb>\tikzuml@AC@xa}{% + \def\tikzumlAssemblyConnectorStartAnchor{east}% + \def\tikzumlAssemblyConnectorEndAnchor{west}% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{west}% + \def\tikzumlAssemblyConnectorEndAnchor{east}% + } + }{}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{-|}}{% + \ifthenelse{\tikzuml@AC@xb>\tikzuml@AC@xa}{% + \def\tikzumlAssemblyConnectorStartAnchor{east}% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{west}% + } + \ifthenelse{\tikzuml@AC@yb>\tikzuml@AC@ya}{% + \def\tikzumlAssemblyConnectorEndAnchor{south}% + }{% + \def\tikzumlAssemblyConnectorEndAnchor{north}% + } + }{}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{|-}}{% + \ifthenelse{\tikzuml@AC@xb>\tikzuml@AC@xa}{% + \def\tikzumlAssemblyConnectorEndAnchor{west}% + }{% + \def\tikzumlAssemblyConnectorEndAnchor{east}% + } + \ifthenelse{\tikzuml@AC@yb>\tikzuml@AC@ya}{% + \def\tikzumlAssemblyConnectorStartAnchor{north}% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{south}% + } + }{}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{-|-}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorStartArm}{auto}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorEndArm}{auto}}{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{0.5 * \tikzuml@AC@xa + 0.5 * \tikzuml@AC@xb}% + }{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{\tikzuml@AC@xb+\tikzumlAssemblyConnectorEndArm}% + }% + }{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{\tikzuml@AC@xa+\tikzumlAssemblyConnectorStartArm}% + }% + \pgfmathparse{\tikzuml@AC@middleArm>\tikzuml@AC@xa} + \pgfmathparse{\tikzuml@AC@middleArm>\tikzuml@AC@xb} + \ifthenelse{\lengthtest{\tikzuml@AC@middleArm>\tikzuml@AC@xa}}{% + \def\tikzumlAssemblyConnectorStartAnchor{east}% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{west}% + } + \ifthenelse{\lengthtest{\tikzuml@AC@middleArm>\tikzuml@AC@xb}}{% + \def\tikzumlAssemblyConnectorEndAnchor{east}% + }{% + \def\tikzumlAssemblyConnectorEndAnchor{west}% + } + }{}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{|-|}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorStartArm}{auto}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorEndArm}{auto}}{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{0.5 * \tikzuml@AC@ya + 0.5 * \tikzuml@AC@yb}% + }{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{\tikzuml@AC@yb+\tikzumlAssemblyConnectorEndArm}% + }% + }{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{\tikzuml@AC@ya+\tikzumlAssemblyConnectorStartArm}% + }% + \ifthenelse{\tikzuml@AC@middleArm>\tikzuml@AC@ya}{% + \def\tikzumlAssemblyConnectorStartAnchor{north}% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{south}% + } + \ifthenelse{\tikzuml@AC@middleArm>\tikzuml@AC@yb}{% + \def\tikzumlAssemblyConnectorEndAnchor{north}% + }{% + \def\tikzumlAssemblyConnectorEndAnchor{south}% + } + }{}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorStartAnchorTmp}{}}{% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{\tikzumlAssemblyConnectorStartAnchorTmp}% + }% + \ifthenelse{\equal{\tikzumlAssemblyConnectorEndAnchorTmp}{}}{% + }{% + \def\tikzumlAssemblyConnectorEndAnchor{\tikzumlAssemblyConnectorEndAnchorTmp}% + }% + % + \node[inner sep=0] (\tikzumlAssemblyConnectorStart@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp) at (\tikzumlAssemblyConnectorStart@nodeName.\tikzumlAssemblyConnectorStartAnchor) {};% + \node[inner sep=0] (\tikzumlAssemblyConnectorEnd@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp) at (\tikzumlAssemblyConnectorEnd@nodeName.\tikzumlAssemblyConnectorEndAnchor) {};% + % + \umlrelation[style={tikzuml connector style}, #1]{\tikzumlAssemblyConnectorStart@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp}{\tikzumlAssemblyConnectorEnd@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorWithPort}{tikzumlTrue}}{% + \node[inner sep=0.5*\tikzumlAssemblyConnectorWidth, rectangle, draw=\tikzumlAssemblyConnectorDrawColor, fill=\tikzumlAssemblyConnectorPortFillColor] (\tikzumlAssemblyConnectorStart@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port) at (\tikzumlAssemblyConnectorStart@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp) {};% + \node[inner sep=0.5*\tikzumlAssemblyConnectorWidth, rectangle, draw=\tikzumlAssemblyConnectorDrawColor, fill=\tikzumlAssemblyConnectorPortFillColor] (\tikzumlAssemblyConnectorEnd@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port) at (\tikzumlAssemblyConnectorEnd@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp) {};% + }{% + \node[inner sep=0] (\tikzumlAssemblyConnectorStart@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port) at (\tikzumlAssemblyConnectorStart@nodeName.\tikzumlAssemblyConnectorStartAnchor) {};% + \node[inner sep=0] (\tikzumlAssemblyConnectorEnd@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port) at (\tikzumlAssemblyConnectorEnd@nodeName.\tikzumlAssemblyConnectorEndAnchor) {};% + }% + % + \addtocounter{tikzumlRelationNum}{-1}% + \ifthenelse{\equal{\tikzumlAssemblyConnectorName}{connector-\thetikzumlConnectorNum}}{% + \edef\tikzumlAssemblyConnectorName{relation-\thetikzumlRelationNum}% + \edef\tikzumlAssemblyConnectorSymbolName{\tikzumlAssemblyConnectorLabel@nodeName}% + }{% + \edef\tikzumlAssemblyConnectorSymbolName{\tikzumlAssemblyConnectorName}% + }% + % + \stepcounter{tikzumlRelationNum}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{--}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorStart@nodeName}{\tikzumlAssemblyConnectorEnd@nodeName}% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{-|}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorFirstArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorStart@nodeName}{\tikzumlAssemblyConnectorName-2}% + }{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-2}{\tikzumlAssemblyConnectorEnd@nodeName}% + }% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{|-}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorFirstArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorStart@nodeName}{\tikzumlAssemblyConnectorName-2}% + }{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-2}{\tikzumlAssemblyConnectorEnd@nodeName}% + }% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{-|-}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorFirstArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorStart@nodeName}{\tikzumlAssemblyConnectorName-2}% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorLastArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-4}{\tikzumlAssemblyConnectorEnd@nodeName}% + }{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-2}{\tikzumlAssemblyConnectorName-4}% + }% + }% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{|-|}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorFirstArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorStart@nodeName}{\tikzumlAssemblyConnectorName-2}% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorLastArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-4}{\tikzumlAssemblyConnectorEnd@nodeName}% + }{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-2}{\tikzumlAssemblyConnectorName-4}% + }% + }% + }{}% + }% + }% + }% + }% + \stepcounter{tikzumlConnectorNum}% +}% +% +% shortcuts of \umlassemblyconnector +\newcommand{\umlHVassemblyconnector}[3][]{% + \pgfkeys{/tikzuml/HVassemblyconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVassemblyconnector, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/HVassemblyconnector/.cd, #1}% + \umlassemblyconnector[geometry=-|, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHassemblyconnector}[3][]{% + \pgfkeys{/tikzuml/VHassemblyconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHassemblyconnector, forbidden option geometry}% + }{}% + }% + }% + \pgfkeys{/tikzuml/VHassemblyconnector/.cd, #1}% + \umlassemblyconnector[geometry=|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVHassemblyconnector}[3][]{% + \pgfkeys{/tikzuml/HVHassemblyconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHassemblyconnector, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/HVHassemblyconnector/.cd, #1}% + \umlassemblyconnector[geometry=-|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHVassemblyconnector}[3][]{% + \pgfkeys{/tikzuml/VHVassemblyconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVassemblyconnector, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/VHVassemblyconnector/.cd, #1}% + \umlassemblyconnector[geometry=|-|, #1]{#2}{#3}% +}% +% +\newcommand{\umlport}[3][]{% + \pgfkeys{/tikzuml/port/.cd, draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlPortDefaultFillColor,% + width/.initial=\tikzumlPortDefaultWidth,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlport forbidden option \keyname}% + }% + }% + \pgfkeys{/tikzuml/port/.cd, #1}% + \pgfkeys{/tikzuml/port/.cd, width/.get=\tikzumlPortWidth,% + draw/.get=\tikzumlPortDrawColor, fill/.get=\tikzumlPortFillColor}% + \edef\tikzumlPort@name{#2}% + \edef\tikzumlPort@anchor{#3}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlPort@nodeName{\tikzumlPort@name}}\x% + % + \node[inner sep=0.5*\tikzumlPortWidth, rectangle, draw=\tikzumlPortDrawColor, fill=\tikzumlPortFillColor] (\tikzumlPort@nodeName-\tikzumlPort@anchor-port) at (\tikzumlPort@nodeName.\tikzumlPort@anchor) {};% +}% +% +\newcommand{\umldelegateconnector}[3][]{% + \def\tikzumlDelegateConnectorWithStartPort{tikzumlFalse}% + \def\tikzumlDelegateConnectorWithEndPort{tikzumlFalse}% + \pgfkeys{/tikzuml/delegateconnector/.cd, + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umldelegateconnector, forbidden option stereo}% + }{}% + }}% + \pgfkeys{/tikzuml/delegateconnector/.cd, #1}% + \umlrelation[style={tikzuml connector style}, stereo=delegate, #1]{#2}{#3}% +}% +% +% shortcuts of \umldelegateconnector +\newcommand{\umlHVdelegateconnector}[3][]{% + \pgfkeys{/tikzuml/HVdelegateconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}\OR\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVdelegateconnector, forbidden option \keyname}% + }{}% + }}% + \pgfkeys{/tikzuml/HVdelegateconnector/.cd, #1}% + \umldelegateconnector[geometry=-|, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHdelegateconnector}[3][]{% + \pgfkeys{/tikzuml/VHdelegateconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}\OR\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHdelegateconnector, forbidden option \keyname}% + }{}% + }}% + \pgfkeys{/tikzuml/VHdelegateconnector/.cd, #1}% + \umldelegateconnector[geometry=|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVHdelegateconnector}[3][]{% + \pgfkeys{/tikzuml/HVHdelegateconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}\OR\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVHdelegateconnector, forbidden option \keyname}% + }{}% + }}% + \pgfkeys{/tikzuml/HVHdelegateconnector/.cd, #1}% + \umldelegateconnector[geometry=-|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHVdelegateconnector}[3][]{% + \pgfkeys{/tikzuml/VHVdelegateconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}\OR\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHVdelegateconnector, forbidden option \keyname}% + }{}% + }}% + \pgfkeys{/tikzuml/VHVdelegateconnector/.cd, #1}% + \umldelegateconnector[geometry=|-|, #1]{#2}{#3}% +}% +%%% End of tikz-uml.sty +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \ No newline at end of file