diff --git a/packages/preview/smartart/0.1.0/LICENSE b/packages/preview/smartart/0.1.0/LICENSE new file mode 100644 index 0000000000..d159169d10 --- /dev/null +++ b/packages/preview/smartart/0.1.0/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/packages/preview/smartart/0.1.0/README.md b/packages/preview/smartart/0.1.0/README.md new file mode 100644 index 0000000000..62d65485aa --- /dev/null +++ b/packages/preview/smartart/0.1.0/README.md @@ -0,0 +1,249 @@ +# smartart + +SmartArt-style diagrams for Typst: card-table, process, gantt, pyramid, hierarchy, steps, venn, timeline, cycle, tree, matrix, and more. + +## Installation + +```typst +#import "@preview/smartart:0.1.0": * +``` + +## Widgets + +### card-table +A card-style table with rounded cells, coloured header, and alternating row fills. + +```typst +#card-table( + ("Study", "Approach", "Dataset"), + ( + ("Guo et al.", "Propose two...", "Homemade"), + ("Gurunathan", "Exhaustive...", "PUT Vein"), + ("Zhang et al.", "ML-based", "InHouse"), + ), +) +``` + +![Screenshot of the card-table widget showing a styled data table with headers and rows](images/card-table.png) + +### gantt +A Gantt chart with task bars, progress indicator, grid lines, and a "today" marker. + +```typst +#gantt( + ( + ("Planning", 0, 12, 100), + ("Development", 6, 30, 60), + ("Testing", 24, 40, 30), + ("Papers", 0, 48, 20, false), + ), + span: 48, grid-step: 12, + periods: ("Year 1", "Year 2", "Year 3", "Year 4"), + today: 20, +) +``` + +![Screenshot of the gantt chart widget with task bars, progress indicators, and a today marker](images/gantt.png) + +### process +A heartbeat-ring process diagram with icon badges and connector arms. Requires [Font Awesome 7 Free](https://fontawesome.com). + +```typst +#process( + ( + ("\u{f02d}", "1. Review methods"), + ("\u{f51e}", "2. Data collection"), + ("\u{f83e}", "3. Signal analysis"), + ), +) +``` + +![Screenshot of the process diagram widget showing a heartbeat ring with connected icon badges](images/process.png) + +### pyramid +A pyramid or funnel diagram (use `flip: true` for a funnel). + +```typst +#pyramid(([Vision], [Strategy], [Tactics])) +``` + +![Screenshot of the pyramid widget showing stacked triangular levels](images/pyramid.png) + +### hierarchy +A root node with a row of children beneath it. + +```typst +#hierarchy([CEO], (([Eng], rgb("#a7dd9b")), [Sales], [HR])) +``` + +![Screenshot of the hierarchy widget showing a root node with child nodes beneath](images/hierarchy.png) + +### steps +A vertical numbered list of steps. + +```typst +#steps(([Plan], [Execute], [Review], ([Deploy], rgb("#e64553")))) +``` + +![Screenshot of the steps widget showing vertical numbered step blocks](images/steps.png) + +### venn +A 2- or 3-circle Venn diagram. + +```typst +#venn((([Behavior], rgb("#aab4f7")), [Signal], [Security])) +``` + +![Screenshot of the venn diagram widget showing three overlapping circles](images/venn.png) + +### hlist +A horizontal row of equally sized coloured blocks. + +```typst +#hlist(([Plan], [Execute], ([Review], rgb("#e64553")))) +``` + +![Screenshot of the hlist widget showing equally sized coloured blocks in a row](images/hlist.png) + +### chevron +A sequence of right-pointing chevron arrows. + +```typst +#chevron(([Phase 1], [Phase 2], [Phase 3], [Phase 4])) +``` + +![Screenshot of the chevron widget showing sequential right-pointing arrow blocks](images/chevron.png) + +### cycle +Nodes arranged on a ring connected by curved arrows. + +```typst +#cycle(([Plan], [Do], [Check], ([Act], rgb("#e64553")))) +``` + +![Screenshot of the cycle widget showing nodes arranged on a ring with curved arrows](images/cycle.png) + +### target +Concentric rings (bullseye). + +```typst +#target(([Vision], [Mission], [Goal])) +``` + +![Screenshot of the target widget showing concentric rings in a bullseye pattern](images/target.png) + +### matrix +A 2x2 matrix with optional axis labels. + +```typst +#matrix(([TL], [TR], [BL], [BR]), + x-axis: ("Low", "High"), + y-axis: ("High", "Low")) +``` + +![Screenshot of the matrix widget showing a 2x2 grid with axis labels](images/matrix.png) + +### snake +A bending process that wraps into multiple rows. + +```typst +#snake(([Step 1], [Step 2], [Step 3], [Step 4], [Step 5], [Step 6])) +``` + +![Screenshot of the snake widget showing a bending process that wraps into rows](images/snake.png) + +### stairs +An ascending stair-step process. + +```typst +#stairs(([Plan], [Develop], ([Launch], rgb("#e64553")))) +``` + +![Screenshot of the stairs widget showing an ascending stair-step process](images/stairs.png) + +### gears +Up to 3 interlocking cogwheels. + +```typst +#gears(([Research], [Development], [Production])) +``` + +![Screenshot of the gears widget showing interlocking cogwheels](images/gears.png) + +### timeline +A horizontal timeline with alternating labels. + +```typst +#timeline((("2024", [Start]), ("2025", [Pilot]), ("2026", [Launch]))) +``` + +![Screenshot of the timeline widget showing milestones along a horizontal line](images/timeline.png) + +### tree +A multi-level tree hierarchy. + +```typst +#tree(([CEO], (([Eng], ([Web], [Mobile])), [Sales]))) +``` + +![Screenshot of the tree widget showing a multi-level hierarchy chart](images/tree.png) + +### arrows +Converging or diverging arrows around a central core. + +```typst +// converging +#arrows(([Idea 1], [Idea 2], [Idea 3]), core: [Core]) + +// diverging +#arrows(([Branch 1], [Branch 2]), diverge: true) +``` + +![Screenshot of converging arrows widget showing arrows pointing inward to a central core](images/arrows-converging.png) +![Screenshot of diverging arrows widget showing arrows pointing outward from a central core](images/arrows-diverging.png) + +### opposing +Two block arrows facing each other. + +```typst +#opposing(([Pros], [Cons])) +``` + +![Screenshot of the opposing widget showing two block arrows facing each other](images/opposing.png) + +### equation +An A + B = C equation-style diagram. + +```typst +#equation(([Input 1], [Input 2], [Output])) +``` + +![Screenshot of the equation widget showing an A plus B equals C diagram](images/equation.png) + +### kpi +Stat cards displaying a large value and a caption. + +```typst +#kpi((("< 1%", [Target EER]), + ("99%", [Accuracy], rgb("#40a02b")), + ("500+", [Users]))) +``` + +![Screenshot of the kpi widget showing stat cards with big values and captions](images/kpi.png) + +### pill-steps +A central hub with connected pill-shaped step rows. Requires [Font Awesome 7 Free](https://fontawesome.com) for icons. + +```typst +#pill-steps( + ( + ([Research], [Gather requirements], + text(font: "Font Awesome 7 Free", size: 26pt)[\u{f0eb}]), + ([Develop], [Build the solution], + text(font: "Font Awesome 7 Free", size: 26pt)[\u{f0ae}], rgb("#74c7ec")), + ), + title: [PROJECT STEPS], +) +``` + +![Screenshot of the pill-steps widget showing a central hub with connected pill-shaped step rows](images/pill-steps.png) diff --git a/packages/preview/smartart/0.1.0/arrows.typ b/packages/preview/smartart/0.1.0/arrows.typ new file mode 100644 index 0000000000..509eeadc26 --- /dev/null +++ b/packages/preview/smartart/0.1.0/arrows.typ @@ -0,0 +1,41 @@ +// Converging or diverging arrows to/from a centre. Import with: #import "arrows.typ": arrows +// +// #arrows(([Idea 1], [Idea 2], [Idea 3]), core: [Core]) +// #arrows(([Branch 1], [Branch 2]), diverge: true) +#import "@preview/cetz:0.5.2": canvas, draw +#import "common.typ": palette, _lab, _col + +#let arrows( + items, + core: none, + diverge: false, + radius: 4.8, + bw: 3.6, bh: 1.6, + text-fill: white, size: 16pt, + core-fill: rgb("#1f1f3a"), core-r: 1.4, +) = canvas({ + import draw: * + let n = items.len() + let ang = i => 90deg - i * 360deg / n + for (i, it) in items.enumerate() { + let a = ang(i) + let col = _col(it, i) + let nearbox = ((radius - 1.1) * calc.cos(a), (radius - 1.1) * calc.sin(a)) + let nearctr = ((core-r + 0.4) * calc.cos(a), (core-r + 0.4) * calc.sin(a)) + if diverge { + line(nearctr, nearbox, mark: (end: ">", fill: col), stroke: col + 6pt) + } else { + line(nearbox, nearctr, mark: (end: ">", fill: col), stroke: col + 6pt) + } + let bx = (radius * calc.cos(a), radius * calc.sin(a)) + rect((bx.at(0) - bw / 2, bx.at(1) - bh / 2), (bx.at(0) + bw / 2, bx.at(1) + bh / 2), + fill: col, stroke: none, radius: 0.15) + content(bx, box(width: (bw - 0.4) * 1cm, + align(center, text(fill: text-fill, weight: "bold", size: size)[#_lab(it)]))) + } + circle((0, 0), radius: core-r, fill: core-fill, stroke: white + 2pt) + if core != none { + content((0, 0), box(width: (2 * core-r - 0.3) * 1cm, + align(center, text(fill: white, weight: "bold", size: size)[#core]))) + } +}) diff --git a/packages/preview/smartart/0.1.0/chevron.typ b/packages/preview/smartart/0.1.0/chevron.typ new file mode 100644 index 0000000000..2dd0136f81 --- /dev/null +++ b/packages/preview/smartart/0.1.0/chevron.typ @@ -0,0 +1,27 @@ +// Chevron: sequential right-pointing arrows. Import with: #import "chevron.typ": chevron +// +// #chevron(([Phase 1], [Phase 2], ([Phase 3], rgb("#e64553")))) +#import "@preview/cetz:0.5.2": canvas, draw +#import "common.typ": palette, _lab, _col + +#let chevron( + items, + w: 5, + h: 2.4, + notch: 0.9, + text-fill: white, + size: 18pt, +) = canvas({ + import draw: * + let n = items.len() + let step = w - notch + let x0 = -(n * step) / 2 + for (i, it) in items.enumerate() { + let x = x0 + i * step + line((x, -h / 2), (x + w - notch, -h / 2), (x + w, 0), + (x + w - notch, h / 2), (x, h / 2), (x + notch, 0), + close: true, fill: _col(it, i), stroke: white + 1.5pt) + content((x + w / 2 + notch / 2, 0), box(width: (w - notch) * 1cm, + align(center, text(fill: text-fill, weight: "bold", size: size)[#_lab(it)]))) + } +}) diff --git a/packages/preview/smartart/0.1.0/common.typ b/packages/preview/smartart/0.1.0/common.typ new file mode 100644 index 0000000000..09478d79ac --- /dev/null +++ b/packages/preview/smartart/0.1.0/common.typ @@ -0,0 +1,11 @@ +// Shared utilities: colour palette and item-accessor helpers. +// Import with: #import "common.typ": palette, _lab, _col +#let palette = ( + rgb("#aab4f7"), // lavender + rgb("#a7dd9b"), // green + rgb("#f9c74f"), // amber + rgb("#e64553"), // red + rgb("#74c7ec"), // sky +) +#let _lab(it) = if type(it) == array { it.at(0) } else { it } +#let _col(it, i) = if type(it) == array and it.len() > 1 { it.at(1) } else { palette.at(calc.rem(i, palette.len())) } diff --git a/packages/preview/smartart/0.1.0/cycle.typ b/packages/preview/smartart/0.1.0/cycle.typ new file mode 100644 index 0000000000..7e6c647dc9 --- /dev/null +++ b/packages/preview/smartart/0.1.0/cycle.typ @@ -0,0 +1,33 @@ +// Cycle: nodes on a ring joined by curved arrows. Import with: #import "cycle.typ": cycle +// +// #cycle(([Plan], [Do], [Check], ([Act], rgb("#e64553")))) +#import "@preview/cetz:0.5.2": canvas, draw +#import "common.typ": palette, _lab, _col + +#let cycle( + items, + radius: auto, + node: 1.5, + arrow: rgb("#73c25a"), + text-fill: white, + size: 16pt, +) = canvas({ + import draw: * + let n = items.len() + let R = if radius == auto { calc.max(3, n * 0.9) } else { radius } + let ang = i => 90deg - i * 360deg / n + let gapd = calc.asin(calc.min(0.99, node / R)) + 7deg + for i in range(n) { + let a0 = ang(i) - gapd + let a1 = ang(calc.rem(i + 1, n)) + gapd + if a1 > a0 { a1 = a1 - 360deg } + arc((R * calc.cos(a0), R * calc.sin(a0)), start: a0, stop: a1, radius: R, + mark: (end: ">", fill: arrow), stroke: arrow + 3pt) + } + for (i, it) in items.enumerate() { + let p = (R * calc.cos(ang(i)), R * calc.sin(ang(i))) + circle(p, radius: node, fill: _col(it, i), stroke: white + 2pt) + content(p, box(width: (2 * node - 0.5) * 1cm, + align(center, text(fill: text-fill, weight: "bold", size: size)[#_lab(it)]))) + } +}) diff --git a/packages/preview/smartart/0.1.0/equation.typ b/packages/preview/smartart/0.1.0/equation.typ new file mode 100644 index 0000000000..31e2522700 --- /dev/null +++ b/packages/preview/smartart/0.1.0/equation.typ @@ -0,0 +1,27 @@ +// Equation: A + B = C (last item is result). Import with: #import "equation.typ": equation +// +// #equation(([Input 1], [Input 2], [Output])) +#import "@preview/cetz:0.5.2": canvas, draw +#import "common.typ": palette, _lab, _col + +#let equation( + items, + w: 3.6, h: 2.4, gap: 1.6, + text-fill: white, size: 18pt, op-size: 32pt, + op-fill: rgb("#1f1f3a"), radius: 0.15, +) = canvas({ + import draw: * + let n = items.len() + let pitch = w + gap + let x0 = -((n - 1) * pitch) / 2 + for (i, it) in items.enumerate() { + let cx = x0 + i * pitch + if i > 0 { + content((cx - pitch / 2, 0), + text(fill: op-fill, weight: "bold", size: op-size)[#(if i == n - 1 { "=" } else { "+" })]) + } + rect((cx - w / 2, -h / 2), (cx + w / 2, h / 2), fill: _col(it, i), stroke: none, radius: radius) + content((cx, 0), box(width: (w - 0.4) * 1cm, + align(center, text(fill: text-fill, weight: "bold", size: size)[#_lab(it)]))) + } +}) diff --git a/packages/preview/smartart/0.1.0/gantt.typ b/packages/preview/smartart/0.1.0/gantt.typ new file mode 100644 index 0000000000..2bb6b29fd0 --- /dev/null +++ b/packages/preview/smartart/0.1.0/gantt.typ @@ -0,0 +1,120 @@ +// Reusable Gantt chart. Import with: #import "gantt.typ": gantt +// +// Works in any unit: pass `span` as a total number of months OR years, and +// `periods` as the axis headers spread evenly across that span. +// +// Months: gantt(tasks, span: 48, grid-step: 6, today: 7, +// periods: ("Year 1", "Year 2", "Year 3", "Year 4")) +// Years: gantt(tasks, span: 4, grid-step: 1, today: 0.5, +// periods: ("Y1", "Y2", "Y3", "Y4")) +// +// Each task is a tuple; only the first three are required: +// (name, start, end) -> progress 0%, highlighted +// (name, start, end, progress) -> progress bar, highlighted +// (name, start, end, progress, group) -> group=false renders it greyed (e.g. papers) +#let gantt( + tasks, + span: 48, + grid-step: auto, // faint vertical line every N units; auto = one per period boundary + today: none, // dashed marker + label at this unit (none = off) + periods: (), // axis header labels, spread evenly across the span + accent: rgb("#40a02b"), // main highlighted-task colour + base-color: luma(210), // bar fill for non-highlighted tasks + done-color: rgb("#4c4f69"),// progress fill for non-highlighted tasks + // ---- colour overrides (auto = derive from the colours above) ---- + progress-color: auto, // filled progress portion of every bar + track-color: auto, // unfilled bar (auto = accent lightened) + row-gap: 0pt, // vertical space between task rows + period-gap: 0pt, // vertical space between period headers and first task + name-fr: 15, // width ratio of the task-name column + bar-fr: 33, // width ratio of the timeline column + gutter: 10pt, +) = { + let N = span + let x = m => (m / N) * 100% + let cols = (name-fr * 1fr, bar-fr * 1fr) + let track(t) = { + set text(hyphenate: false) + let name = t.at(0) + let s = t.at(1) + let e = t.at(2) + let prog = t.at(3, default: 0) + let grp = t.at(4, default: true) + let base = if track-color != auto { track-color } else if grp { accent.lighten(45%) } else { base-color } + let done = if progress-color != auto { progress-color } else if grp { accent } else { done-color } + grid(columns: cols, align: (right + horizon, left + horizon), + column-gutter: gutter, inset: (y: 1pt), + text(size: 21pt, fill: luma(60))[#name], + box(width: 100%, height: 0.8cm)[ + #place(left + horizon, stack(dir: ltr, + box(width: x(s)), + box(width: (e - s) / N * 100%, height: 0.62cm, radius: 3pt, fill: base, + stack(dir: ltr, box(width: prog * 1%, height: 100%, radius: 3pt, fill: done))))) + #{ + let lbl = text(size: 18pt, weight: "bold", fill: luma(80))[#prog%] + // bars reaching the end have no room on the right -> tuck label inside + if e < N { place(left + horizon, dx: x(e) + 8pt, lbl) } + else { place(right + horizon, dx: -4pt, lbl) } + } + ]) + } + block({ + set block(above: 0pt, below: 0pt) + // period header (evenly spaced labels) + if periods.len() > 0 { + let seg = N / periods.len() + grid(columns: cols, column-gutter: gutter, + [], box(width: 100%, height: 0.9cm)[ + #for (i, lbl) in periods.enumerate() { + place(top + left, dx: x(i * seg + seg / 2), + move(dx: -3cm, box(width: 6cm)[ + #align(center)[#text(size: 20pt, weight: "bold")[#lbl]]])) + } + ]) + v(period-gap) + } + // tasks with continuous grid lines drawn once over the whole stack + layout(size => { + let avail = size.width - gutter + let total-fr = name-fr + bar-fr + let mx = m => avail * name-fr / total-fr + gutter + avail * bar-fr / total-fr * (m / N) + let body = { + let first = true + for t in tasks { + if first { first = false } else { v(row-gap) } + track(t) + } + } + // measure at the real width so fr columns resolve and wrapping matches the render + let h = measure(box(width: size.width, body)).height + // grid-step: auto -> one line per period boundary; a number -> every N units + let lines = () + if grid-step == auto { + let n = calc.max(periods.len(), 1) + for i in range(1, n) { lines.push(i * N / n) } + } else { + let m = grid-step + while m < N { lines.push(m); m = m + grid-step } + } + block(width: 100%, { + for m in lines { + place(top + left, dx: mx(m), line(length: h, angle: 90deg, + stroke: (paint: luma(210), thickness: 0.8pt))) + } + if today != none { + place(top + left, dx: mx(today), line(length: h, angle: 90deg, + stroke: (paint: done-color, thickness: 4pt, dash: (array: (14pt, 9pt))))) + } + body + }) + }) + if today != none { + v(period-gap) + grid(columns: cols, column-gutter: gutter, + [], box(width: 100%, height: 0.8cm)[ + #place(top + left, dx: x(today), move(dx: -1.2cm, + text(size: 20pt, weight: "bold", fill: done-color)[Today])) + ]) + } + }) +} diff --git a/packages/preview/smartart/0.1.0/gears.typ b/packages/preview/smartart/0.1.0/gears.typ new file mode 100644 index 0000000000..b0ed690e3e --- /dev/null +++ b/packages/preview/smartart/0.1.0/gears.typ @@ -0,0 +1,35 @@ +// Gears: up to 3 interlocking cogs. Import with: #import "gears.typ": gears +// +// #gears(([Research], [Development], ([Production], rgb("#e64553")))) +#import "@preview/cetz:0.5.2": canvas, draw +#import "common.typ": palette, _lab, _col + +#let gears( + items, + r: 2.6, teeth: 10, + text-fill: rgb("#1f1f3a"), size: 15pt, +) = canvas({ + import draw: * + let n = calc.min(items.len(), 3) + let d = r * 1.62 + let step = 360deg / teeth + let cog(cx, cy, fill, phase) = { + let pts = () + let rin = r * 0.80 + for t in range(teeth) { + let a = phase + t * step + pts.push((cx + rin * calc.cos(a), cy + rin * calc.sin(a))) + pts.push((cx + r * calc.cos(a + step * 0.25), cy + r * calc.sin(a + step * 0.25))) + pts.push((cx + r * calc.cos(a + step * 0.5), cy + r * calc.sin(a + step * 0.5))) + pts.push((cx + rin * calc.cos(a + step * 0.75), cy + rin * calc.sin(a + step * 0.75))) + } + line(..pts, close: true, fill: fill, stroke: none) + circle((cx, cy), radius: r * 0.32, fill: white) + } + for (i, it) in items.slice(0, n).enumerate() { + let cx = i * d - (n - 1) * d / 2 + cog(cx, 0, _col(it, i), if calc.rem(i, 2) == 0 { 0deg } else { step / 2 }) + content((cx, 0), box(width: r * 0.7 * 1cm, + align(center, text(fill: text-fill, weight: "bold", size: size)[#_lab(it)]))) + } +}) diff --git a/packages/preview/smartart/0.1.0/hierarchy.typ b/packages/preview/smartart/0.1.0/hierarchy.typ new file mode 100644 index 0000000000..1c159a2e75 --- /dev/null +++ b/packages/preview/smartart/0.1.0/hierarchy.typ @@ -0,0 +1,39 @@ +// Hierarchy diagram: one root over a single row of children. +// Import with: #import "hierarchy.typ": hierarchy +// +// #hierarchy([CEO], (([Eng], rgb("#a7dd9b")), [Sales], [HR])) +#import "@preview/cetz:0.5.2": canvas, draw +#import "common.typ": palette, _lab, _col + +#let hierarchy( + root, + children, + root-fill: rgb("#aab4f7"), + box-fill: rgb("#ebedf5"), + text-fill: rgb("#1f1f3a"), + accent: rgb("#73c25a"), + box-w: 5.2, + box-h: 1.7, + gap-x: 1.0, + level-gap: 3.2, + size: 18pt, +) = canvas({ + import draw: * + let n = children.len() + let span = n * box-w + (n - 1) * gap-x + let cx = i => -span / 2 + box-w / 2 + i * (box-w + gap-x) + let cy = -level-gap + let node(p, lab, fill) = { + rect((p.at(0) - box-w / 2, p.at(1) - box-h / 2), + (p.at(0) + box-w / 2, p.at(1) + box-h / 2), fill: fill, stroke: none, radius: 0.18) + content(p, box(width: (box-w - 0.4) * 1cm, + align(center, text(size: size, fill: text-fill)[#lab]))) + } + let midy = -level-gap / 2 + for i in range(n) { + line((0, -box-h / 2), (0, midy), (cx(i), midy), (cx(i), cy + box-h / 2), + stroke: (paint: accent, thickness: 3pt, join: "round")) + } + node((0, 0), root, root-fill) + for (i, it) in children.enumerate() { node((cx(i), cy), _lab(it), _col(it, i)) } +}) diff --git a/packages/preview/smartart/0.1.0/hlist.typ b/packages/preview/smartart/0.1.0/hlist.typ new file mode 100644 index 0000000000..bbf2d55200 --- /dev/null +++ b/packages/preview/smartart/0.1.0/hlist.typ @@ -0,0 +1,20 @@ +// Horizontal list: equal blocks side by side. Import with: #import "hlist.typ": hlist +// +// #hlist(([Plan], [Execute], ([Review], rgb("#e64553")))) +#import "common.typ": palette, _lab, _col + +#let hlist( + items, + text-fill: white, + size: 18pt, + height: 3cm, + radius: 8pt, + gutter: 10pt, + inset: 12pt, +) = { + set par(justify: false, leading: 0.5em) + grid(columns: items.map(_ => 1fr), column-gutter: gutter, + ..items.enumerate().map(((i, it)) => + box(width: 100%, height: height, fill: _col(it, i), radius: radius, inset: inset, + align(center + horizon, text(fill: text-fill, weight: "bold", size: size)[#_lab(it)])))) +} diff --git a/packages/preview/smartart/0.1.0/images/arrows-converging.png b/packages/preview/smartart/0.1.0/images/arrows-converging.png new file mode 100644 index 0000000000..1878baf3e8 Binary files /dev/null and b/packages/preview/smartart/0.1.0/images/arrows-converging.png differ diff --git a/packages/preview/smartart/0.1.0/images/arrows-diverging.png b/packages/preview/smartart/0.1.0/images/arrows-diverging.png new file mode 100644 index 0000000000..e5de2a7975 Binary files /dev/null and b/packages/preview/smartart/0.1.0/images/arrows-diverging.png differ diff --git a/packages/preview/smartart/0.1.0/images/card-table.png b/packages/preview/smartart/0.1.0/images/card-table.png new file mode 100644 index 0000000000..74c227f445 Binary files /dev/null and b/packages/preview/smartart/0.1.0/images/card-table.png differ diff --git a/packages/preview/smartart/0.1.0/images/chevron.png b/packages/preview/smartart/0.1.0/images/chevron.png new file mode 100644 index 0000000000..f8d72e80f4 Binary files /dev/null and b/packages/preview/smartart/0.1.0/images/chevron.png differ diff --git a/packages/preview/smartart/0.1.0/images/cycle.png b/packages/preview/smartart/0.1.0/images/cycle.png new file mode 100644 index 0000000000..6876cafca2 Binary files /dev/null and b/packages/preview/smartart/0.1.0/images/cycle.png differ diff --git a/packages/preview/smartart/0.1.0/images/equation.png b/packages/preview/smartart/0.1.0/images/equation.png new file mode 100644 index 0000000000..919dafac16 Binary files /dev/null and b/packages/preview/smartart/0.1.0/images/equation.png differ diff --git a/packages/preview/smartart/0.1.0/images/gantt.png b/packages/preview/smartart/0.1.0/images/gantt.png new file mode 100644 index 0000000000..8f4c4e63ae Binary files /dev/null and b/packages/preview/smartart/0.1.0/images/gantt.png differ diff --git a/packages/preview/smartart/0.1.0/images/gears.png b/packages/preview/smartart/0.1.0/images/gears.png new file mode 100644 index 0000000000..d65e84edee Binary files /dev/null and b/packages/preview/smartart/0.1.0/images/gears.png differ diff --git a/packages/preview/smartart/0.1.0/images/hierarchy.png b/packages/preview/smartart/0.1.0/images/hierarchy.png new file mode 100644 index 0000000000..20a9a838f1 Binary files /dev/null and b/packages/preview/smartart/0.1.0/images/hierarchy.png differ diff --git a/packages/preview/smartart/0.1.0/images/hlist.png b/packages/preview/smartart/0.1.0/images/hlist.png new file mode 100644 index 0000000000..6deb289c7d Binary files /dev/null and b/packages/preview/smartart/0.1.0/images/hlist.png differ diff --git a/packages/preview/smartart/0.1.0/images/kpi.png b/packages/preview/smartart/0.1.0/images/kpi.png new file mode 100644 index 0000000000..0493905f58 Binary files /dev/null and b/packages/preview/smartart/0.1.0/images/kpi.png differ diff --git a/packages/preview/smartart/0.1.0/images/matrix.png b/packages/preview/smartart/0.1.0/images/matrix.png new file mode 100644 index 0000000000..2ee852e684 Binary files /dev/null and b/packages/preview/smartart/0.1.0/images/matrix.png differ diff --git a/packages/preview/smartart/0.1.0/images/opposing.png b/packages/preview/smartart/0.1.0/images/opposing.png new file mode 100644 index 0000000000..fc04a5a56f Binary files /dev/null and b/packages/preview/smartart/0.1.0/images/opposing.png differ diff --git a/packages/preview/smartart/0.1.0/images/pill-steps.png b/packages/preview/smartart/0.1.0/images/pill-steps.png new file mode 100644 index 0000000000..cc3d0085e5 Binary files /dev/null and b/packages/preview/smartart/0.1.0/images/pill-steps.png differ diff --git a/packages/preview/smartart/0.1.0/images/process.png b/packages/preview/smartart/0.1.0/images/process.png new file mode 100644 index 0000000000..1957dcc5fb Binary files /dev/null and b/packages/preview/smartart/0.1.0/images/process.png differ diff --git a/packages/preview/smartart/0.1.0/images/pyramid.png b/packages/preview/smartart/0.1.0/images/pyramid.png new file mode 100644 index 0000000000..b0ace3ecc5 Binary files /dev/null and b/packages/preview/smartart/0.1.0/images/pyramid.png differ diff --git a/packages/preview/smartart/0.1.0/images/snake.png b/packages/preview/smartart/0.1.0/images/snake.png new file mode 100644 index 0000000000..7aabd5930e Binary files /dev/null and b/packages/preview/smartart/0.1.0/images/snake.png differ diff --git a/packages/preview/smartart/0.1.0/images/stairs.png b/packages/preview/smartart/0.1.0/images/stairs.png new file mode 100644 index 0000000000..937318e149 Binary files /dev/null and b/packages/preview/smartart/0.1.0/images/stairs.png differ diff --git a/packages/preview/smartart/0.1.0/images/steps.png b/packages/preview/smartart/0.1.0/images/steps.png new file mode 100644 index 0000000000..bdebaa8d72 Binary files /dev/null and b/packages/preview/smartart/0.1.0/images/steps.png differ diff --git a/packages/preview/smartart/0.1.0/images/target.png b/packages/preview/smartart/0.1.0/images/target.png new file mode 100644 index 0000000000..ee3fb2cdae Binary files /dev/null and b/packages/preview/smartart/0.1.0/images/target.png differ diff --git a/packages/preview/smartart/0.1.0/images/timeline.png b/packages/preview/smartart/0.1.0/images/timeline.png new file mode 100644 index 0000000000..0ca6519ecc Binary files /dev/null and b/packages/preview/smartart/0.1.0/images/timeline.png differ diff --git a/packages/preview/smartart/0.1.0/images/tree.png b/packages/preview/smartart/0.1.0/images/tree.png new file mode 100644 index 0000000000..6dce3fda19 Binary files /dev/null and b/packages/preview/smartart/0.1.0/images/tree.png differ diff --git a/packages/preview/smartart/0.1.0/images/venn.png b/packages/preview/smartart/0.1.0/images/venn.png new file mode 100644 index 0000000000..ecee988258 Binary files /dev/null and b/packages/preview/smartart/0.1.0/images/venn.png differ diff --git a/packages/preview/smartart/0.1.0/kpi.typ b/packages/preview/smartart/0.1.0/kpi.typ new file mode 100644 index 0000000000..fbf2a38237 --- /dev/null +++ b/packages/preview/smartart/0.1.0/kpi.typ @@ -0,0 +1,21 @@ +// KPI stat cards: big value + caption in a row. Import with: #import "kpi.typ": kpi +// +// #kpi((("< 1%", [Target EER]), ([99%], [Accuracy], rgb("#40a02b")))) +#import "common.typ": palette, _lab, _col + +#let kpi( + items, + h: 3.6cm, + value-size: 38pt, label-size: 16pt, + text-fill: white, + gutter: 12pt, radius: 10pt, inset: 14pt, +) = { + set par(justify: false, leading: 0.5em) + grid(columns: items.map(_ => 1fr), column-gutter: gutter, + ..items.enumerate().map(((i, it)) => box( + width: 100%, height: h, radius: radius, inset: inset, + fill: it.at(2, default: palette.at(calc.rem(i, palette.len()))), + align(center + horizon, stack(dir: ttb, spacing: 8pt, + box(text(fill: text-fill, weight: "bold", size: value-size)[#it.at(0)]), + text(fill: text-fill, size: label-size)[#it.at(1)]))))) +} diff --git a/packages/preview/smartart/0.1.0/matrix.typ b/packages/preview/smartart/0.1.0/matrix.typ new file mode 100644 index 0000000000..811560092c --- /dev/null +++ b/packages/preview/smartart/0.1.0/matrix.typ @@ -0,0 +1,41 @@ +// 2×2 matrix with optional axis labels. Import with: #import "matrix.typ": matrix +// +// #matrix(((TL), (TR), (BL), (BR)), x-axis: ("Low", "High"), y-axis: ("High", "Low")) +#import "@preview/cetz:0.5.2": canvas, draw +#import "common.typ": palette, _lab, _col + +#let matrix( + cells, + x-axis: none, + y-axis: none, + cw: 6, + ch: 4, + gutter: 0.3, + text-fill: white, + size: 18pt, + axis-fill: rgb("#1f1f3a"), + radius: 0.12, +) = canvas({ + import draw: * + let hx = (cw + gutter) / 2 + let hy = (ch + gutter) / 2 + let centers = ((-hx, hy), (hx, hy), (-hx, -hy), (hx, -hy)) + for (i, it) in cells.enumerate() { + let c = centers.at(i) + rect((c.at(0) - cw / 2, c.at(1) - ch / 2), (c.at(0) + cw / 2, c.at(1) + ch / 2), + fill: _col(it, i), stroke: none, radius: radius) + content(c, box(width: (cw - 0.8) * 1cm, + align(center, text(fill: text-fill, weight: "bold", size: size)[#_lab(it)]))) + } + let ax(c) = text(fill: axis-fill, weight: "bold", size: size)[#c] + if x-axis != none { + let y = -hy - ch / 2 - 0.5 + content((-hx, y), ax(x-axis.at(0))) + content((hx, y), ax(x-axis.at(1))) + } + if y-axis != none { + let x = -hx - cw / 2 - 0.5 + content((x, hy), ax(y-axis.at(0)), angle: 90deg) + content((x, -hy), ax(y-axis.at(1)), angle: 90deg) + } +}) diff --git a/packages/preview/smartart/0.1.0/opposing.typ b/packages/preview/smartart/0.1.0/opposing.typ new file mode 100644 index 0000000000..e6ded6f031 --- /dev/null +++ b/packages/preview/smartart/0.1.0/opposing.typ @@ -0,0 +1,26 @@ +// Opposing: two block arrows facing each other. Import with: #import "opposing.typ": opposing +// +// #opposing(([Pros], [Cons])) +// #opposing(([Pros], ([Cons], rgb("#e64553")))) +#import "@preview/cetz:0.5.2": canvas, draw +#import "common.typ": palette, _lab, _col + +#let opposing( + items, + w: 6.5, h: 2.6, notch: 1.3, gap: 0.5, + text-fill: white, size: 18pt, +) = canvas({ + import draw: * + let la = items.at(0) + let ra = items.at(1) + let rx = -gap / 2 + line((rx - w, -h / 2), (rx - notch, -h / 2), (rx, 0), (rx - notch, h / 2), (rx - w, h / 2), + close: true, fill: _col(la, 0), stroke: none) + content((rx - w / 2 - notch / 2, 0), box(width: (w - notch) * 1cm, + align(center, text(fill: text-fill, weight: "bold", size: size)[#_lab(la)]))) + let lx = gap / 2 + line((lx + w, -h / 2), (lx + notch, -h / 2), (lx, 0), (lx + notch, h / 2), (lx + w, h / 2), + close: true, fill: _col(ra, 1), stroke: none) + content((lx + w / 2 + notch / 2, 0), box(width: (w - notch) * 1cm, + align(center, text(fill: text-fill, weight: "bold", size: size)[#_lab(ra)]))) +}) diff --git a/packages/preview/smartart/0.1.0/pill-steps.typ b/packages/preview/smartart/0.1.0/pill-steps.typ new file mode 100644 index 0000000000..e46c75e651 --- /dev/null +++ b/packages/preview/smartart/0.1.0/pill-steps.typ @@ -0,0 +1,82 @@ +// Pill-steps: central hub + connected rounded pill rows (freepik style). +// Import with: #import "pill-steps.typ": pill-steps +// +// item = (title, body) | (title, body, icon) | (title, body, icon, color) +// icon is ANY content already styled by you. +// +// #pill-steps(( +// ([Research], [Gather requirements], text(font: "Font Awesome 7 Free", size: 26pt)[\u{f0eb}]), +// ([Develop], [Build the solution], , rgb("#74c7ec")), +// )) +#import "common.typ": palette, _lab, _col + +#let pill-steps( + items, + title: [INFOGRAPHIC \ STEPS], + hub-body: none, + hub-r: 3.6cm, step-r: 1.1cm, + pill-w: 15cm, pill-h: 2.4cm, vgap: 0.6cm, gap-hub: 1.8cm, + text-fill: white, body-fill: white.transparentize(12%), + hub-fill: white, hub-text: rgb("#1f1f3a"), + step-circle-fill: white, step-text-fill: auto, + connector: luma(170), connector-dot-fill: white, dot-stroke: luma(150), + icon-color: auto, + icon-font: "Font Awesome 7 Free", icon-size: 26pt, + title-size: 20pt, body-size: 12pt, step-size: 28pt, steplbl-size: 11pt, +) = { + set par(justify: false, leading: 0.5em) + let n = items.len() + let totalH = n * pill-h + (n - 1) * vgap + let H = calc.max(totalH, 2 * hub-r) + let pillX = 2 * hub-r + gap-hub + let W = pillX + pill-w + let rowTop = i => (H - totalH) / 2 + i * (pill-h + vgap) + let cyf = i => rowTop(i) + pill-h / 2 + let hubCY = H / 2 + let lab = it => if type(it) == array { it.at(0) } else { it } + let bod = it => if type(it) == array { it.at(1, default: []) } else { [] } + let ico = it => if type(it) == array { it.at(2, default: none) } else { none } + let colf = (it, i) => if type(it) == array and it.len() > 3 { it.at(3) } else { palette.at(calc.rem(i, palette.len())) } + let stf = (col, i) => if step-text-fill == auto { col } else { step-text-fill } + box(width: W, height: H, { + for i in range(n) { + let ax = hub-r * 1.35 + let ay = hubCY + (cyf(i) - hubCY) * 0.35 + let bx = pillX - step-r + let by = cyf(i) + let mx = (ax + bx) / 2 + place(top + left, curve(stroke: connector + 1.5pt, + curve.move((ax, ay)), curve.cubic((mx, ay), (mx, by), (bx, by)))) + place(top + left, dx: ax - 3pt, dy: ay - 3pt, + circle(radius: 3pt, fill: connector-dot-fill, stroke: dot-stroke + 1.2pt)) + } + for (i, it) in items.enumerate() { + let col = colf(it, i) + let icon = ico(it) + place(top + left, dx: pillX, dy: rowTop(i), + box(width: pill-w, height: pill-h, fill: col, radius: pill-h / 2, + inset: (left: step-r + 1cm, right: if icon != none { 2.4cm } else { 0.8cm }, y: 0.3cm), + align(horizon, stack(dir: ttb, spacing: 4pt, + text(fill: text-fill, weight: "bold", size: title-size)[#lab(it)], + text(fill: body-fill, size: body-size)[#bod(it)])))) + if icon != none { + let ico-display = if type(icon) == "string" { + text(fill: icon-color, font: icon-font, size: icon-size)[#icon] + } else if icon-color != auto { + { set text(fill: icon-color); icon } + } else { icon } + place(top + left, dx: pillX + pill-w - 1.8cm, dy: cyf(i) - 0.7cm, + box(width: 1.4cm, height: 1.4cm, align(center + horizon, ico-display))) + } + place(top + left, dx: pillX - step-r, dy: cyf(i) - step-r, + box(width: 2 * step-r, height: 2 * step-r, fill: step-circle-fill, radius: step-r, + align(center + horizon, stack(dir: ttb, spacing: 0pt, + text(fill: stf(col, i), weight: "bold", size: step-size)[#(if i + 1 < 10 { "0" })#(i + 1)])))) + } + place(top + left, dx: 0pt, dy: hubCY - hub-r, + box(width: 2 * hub-r, height: 2 * hub-r, fill: hub-fill, radius: hub-r, inset: hub-r * 0.35, + align(center + horizon, stack(dir: ttb, spacing: 6pt, + text(fill: hub-text, weight: "bold", size: title-size + 4pt)[#title], + if hub-body != none { box(width: 100%, text(fill: hub-text.lighten(15%), size: body-size)[#hub-body]) })))) + }) +} diff --git a/packages/preview/smartart/0.1.0/process.typ b/packages/preview/smartart/0.1.0/process.typ new file mode 100644 index 0000000000..7ed97b7808 --- /dev/null +++ b/packages/preview/smartart/0.1.0/process.typ @@ -0,0 +1,116 @@ +// Reusable process / cycle diagram (replica of the methodology.png style). +// Import with: #import "process.typ": process +// +// Requires the "Font Awesome 7 Free" font installed for the icons, and the +// cetz package (auto-downloaded by Typst on first compile). +// +// #process( +// ( +// ("\u{f02d}", "1. Review and selection of existing methods."), // book +// ("\u{f51e}", "2. Data collection."), // coins +// ("\u{f83e}", "3. Analysis of signal patterns."), // wave-square +// ("\u{f023}", "4. Development of the authentication model."), // lock +// ("\u{f46c}", "5. Evaluation and validation of the system."), // clipboard-check +// ("\u{f080}", "6. Documentation and dissemination of results."),// chart-bar +// ), +// ) +// +// Items: 6 tuples (icon-glyph, label). First three render on the left, last +// three on the right (mirrored). Any Font Awesome codepoint works as the glyph. +#import "@preview/cetz:0.5.2": canvas, draw + +#let fa = "Font Awesome 7 Free" + +#let process( + items, + core: text(font: "Font Awesome 7 Free", size: 64pt, fill: rgb("#e64553"))[\u{f21e}], // heartbeat + accent: rgb("#73c25a"), // tab + connector + ring-dot color (medium green) + badge-color: rgb("#a7dd9b"), // icon badge (light green) + icon-color: white, // icon glyph color inside the badge + ring: rgb("#e64553"), // central ring color + box-fill: rgb("#ebedf5"), // pale lavender-grey, like the original + box-radius: 0pt, // square corners, like the original + box-h: 3cm, // fixed height so every card is the same size + ring-radius: 3, +) = { + let lefts = items.slice(0, calc.min(3, items.len())) + let rights = if items.len() > 3 { items.slice(3) } else { () } + let yrows = (4.3, 0, -4.3) + let bx = 9 // box center x + let inner = 5 // box inner edge (where the green tab + line start) = bx - box-w/2 + let box-w = 8cm + let line-thickness = 4pt // connector line thickness + let ring-thickness = 8pt // central ring thickness + + // ---- per-item colour: a tuple may carry an optional 3rd element = its colour. + // none -> fall back to the global green. tab/line/dot use it; badge a lighter tint. + let item-color(it) = it.at(2, default: none) + let tab-color(col) = if col == none { accent } else { col } + let badge-fill(col) = if col == none { badge-color } else { col.lighten(45%) } + + // ---- one item box: label card + overlapping icon badge + inner tab ---- + // badge diameter == box height + let badge(icon, col) = circle(radius: box-h / 2, fill: col, stroke: none, + align(center + horizon, text(font: fa, fill: icon-color, size: box-h * 0.42)[#icon])) + let node(icon, label, mirror, col) = { + let pad-icon = box-h / 2 + 0.3cm // clear the half of the badge that sits inside + let body = box(fill: box-fill, radius: box-radius, width: box-w, height: box-h, + inset: (y: 14pt, + left: if mirror { 0.6cm } else { pad-icon }, + right: if mirror { pad-icon } else { 0.6cm }))[ + #set text(size: 16pt) + #set par(justify: false, leading: 0.5em) + #align(horizon)[#label] // vertical-centre the text in the fixed-height card + ] + box(width: box-w)[ + #body + // tab on the inner edge (toward the centre) + #place((if mirror { left } else { right }) + horizon, + box(width: 0.3cm, height: 100%, fill: tab-color(col))) + // icon badge: diameter == box height, centre aligned to the box outer edge + #place((if mirror { right } else { left }) + horizon, + dx: if mirror { box-h / 2 } else { -box-h / 2 }, badge(icon, badge-fill(col))) + ] + } + + canvas({ + import draw: * + let r = ring-radius + // hollow ring-dot, coloured per item + let hdot(p, col) = circle(p, radius: 0.17, fill: white, stroke: 2.6pt + col) + + // connectors first (behind ring + boxes): short horizontal stub off the box, + // then a diagonal to the ring — the dog-leg shape of the original. + let elbow-x = 4.2 + let ring-dots = () + for (side, group) in ((-1, lefts), (1, rights)) { + for (i, it) in group.enumerate() { + let by = yrows.at(i) + let col = tab-color(item-color(it)) + let tp = (side * inner, by) // inner edge / tab + let el = (side * elbow-x, by) // bend where the stub meets the diagonal + let dx = side * elbow-x + let d = calc.sqrt(dx * dx + by * by) + let rp = (dx / d * r, by / d * r) // point on the ring + // single polyline so the elbow has a clean rounded join (no notch) + line(tp, el, rp, stroke: (paint: col, thickness: line-thickness, join: "round", cap: "round")) + ring-dots.push((rp, col)) + } + } + + // central ring + circle((0, 0), radius: r, stroke: ring-thickness + ring, fill: white) + content((0, 0), core) + + // hollow ring-edge dots drawn last, so they sit on top of the ring + for (rp, col) in ring-dots { hdot(rp, col) } + + // boxes on top of the connector ends + for (i, it) in lefts.enumerate() { + content((-bx, yrows.at(i)), node(it.at(0), it.at(1), false, item-color(it))) + } + for (i, it) in rights.enumerate() { + content((bx, yrows.at(i)), node(it.at(0), it.at(1), true, item-color(it))) + } + }) +} diff --git a/packages/preview/smartart/0.1.0/pyramid.typ b/packages/preview/smartart/0.1.0/pyramid.typ new file mode 100644 index 0000000000..9dacbd9ea7 --- /dev/null +++ b/packages/preview/smartart/0.1.0/pyramid.typ @@ -0,0 +1,30 @@ +// Pyramid (or funnel) diagram. Import with: #import "pyramid.typ": pyramid +// +// #pyramid((([Vision],), [Strategy], ([Tactics], rgb("#e64553")))) +// #pyramid(([Top], [Middle], [Bottom]), flip: true) // funnel +#import "@preview/cetz:0.5.2": canvas, draw +#import "common.typ": palette, _lab, _col + +#let pyramid( + levels, + width: 18, + height: 12, + text-fill: white, + size: 22pt, + gap: 0.18, + flip: false, +) = canvas({ + import draw: * + let n = levels.len() + let hw(y) = (width / 2) * (if flip { y } else { height - y }) / height + for (i, it) in levels.enumerate() { + let col = _col(it, i) + let yt = height * (1 - i / n) - (if i == 0 { 0 } else { gap / 2 }) + let yb = height * (1 - (i + 1) / n) + gap / 2 + line((-hw(yt), yt), (hw(yt), yt), (hw(yb), yb), (-hw(yb), yb), + close: true, fill: col, stroke: white + 2pt) + let yl = if flip { yt - (yt - yb) * 0.32 } else { yb + (yt - yb) * 0.32 } + content((0, yl), box(width: calc.max(1.2, 2 * hw(yl) - 0.6) * 1cm, + align(center, text(fill: text-fill, weight: "bold", size: size)[#_lab(it)]))) + } +}) diff --git a/packages/preview/smartart/0.1.0/smartart.typ b/packages/preview/smartart/0.1.0/smartart.typ new file mode 100644 index 0000000000..b756d29e9a --- /dev/null +++ b/packages/preview/smartart/0.1.0/smartart.typ @@ -0,0 +1,46 @@ +// SmartArt library — one import for every diagram in this poster. +// +// #import "smartart.typ": * +// +// Each diagram lives in its own file under this directory. +// +// Families: +// List steps (vertical numbered) hlist (horizontal blocks) +// Process chevron snake (wrapping) stairs (step-up) timeline +// Cycle cycle +// Hierarchy hierarchy (1 level) tree (multi-level) +// Relationship venn target arrows (converging/diverging) +// opposing equation gears +// Matrix matrix (2x2 + axes) +// Pyramid pyramid (flip: true -> funnel) +// Poster kpi (stat cards) pill-steps (hub + pill rows) arrow-list (nested arrows) +// Table card-table +// Specialised process (heartbeat-ring) gantt +// +// Every diagram takes a list whose items are either a label, or a +// (label, color) tuple to override that item's colour. + +#import "common.typ": _col, _lab, palette + +#import "table.typ": card-row, card-table +#import "gantt.typ": gantt +#import "process.typ": process +#import "pyramid.typ": pyramid +#import "hierarchy.typ": hierarchy +#import "steps.typ": steps +#import "venn.typ": venn +#import "hlist.typ": hlist +#import "chevron.typ": chevron +#import "cycle.typ": cycle +#import "target.typ": target +#import "matrix.typ": matrix +#import "snake.typ": snake +#import "stairs.typ": stairs +#import "gears.typ": gears +#import "timeline.typ": timeline +#import "tree.typ": tree +#import "arrows.typ": arrows +#import "opposing.typ": opposing +#import "equation.typ": equation +#import "kpi.typ": kpi +#import "pill-steps.typ": pill-steps diff --git a/packages/preview/smartart/0.1.0/snake.typ b/packages/preview/smartart/0.1.0/snake.typ new file mode 100644 index 0000000000..c7ed530d1f --- /dev/null +++ b/packages/preview/smartart/0.1.0/snake.typ @@ -0,0 +1,41 @@ +// Snake: bending process that wraps into rows. Import with: #import "snake.typ": snake +// +// #snake(([Step 1], [Step 2], [Step 3], [Step 4], [Step 5], [Step 6])) +#import "@preview/cetz:0.5.2": canvas, draw +#import "common.typ": palette, _lab, _col + +#let snake( + items, + cols: 4, + w: 4.5, h: 2.2, gx: 1.4, gy: 1.4, + accent: rgb("#73c25a"), + text-fill: white, size: 16pt, radius: 0.15, +) = canvas({ + import draw: * + let n = items.len() + let pos = i => { + let row = calc.quo(i, cols) + let inrow = calc.rem(i, cols) + let col = if calc.rem(row, 2) == 0 { inrow } else { cols - 1 - inrow } + (col * (w + gx), -row * (h + gy)) + } + for i in range(n - 1) { + let a = pos(i) + let b = pos(i + 1) + if calc.quo(i, cols) == calc.quo(i + 1, cols) { + let dir = if calc.rem(calc.quo(i, cols), 2) == 0 { 1 } else { -1 } + line((a.at(0) + dir * w / 2, a.at(1)), (b.at(0) - dir * w / 2, b.at(1)), + mark: (end: ">", fill: accent), stroke: accent + 3pt) + } else { + line((a.at(0), a.at(1) - h / 2), (b.at(0), b.at(1) + h / 2), + mark: (end: ">", fill: accent), stroke: accent + 3pt) + } + } + for (i, it) in items.enumerate() { + let c = pos(i) + rect((c.at(0) - w / 2, c.at(1) - h / 2), (c.at(0) + w / 2, c.at(1) + h / 2), + fill: _col(it, i), stroke: none, radius: radius) + content(c, box(width: (w - 0.6) * 1cm, + align(center, text(fill: text-fill, weight: "bold", size: size)[#_lab(it)]))) + } +}) diff --git a/packages/preview/smartart/0.1.0/stairs.typ b/packages/preview/smartart/0.1.0/stairs.typ new file mode 100644 index 0000000000..492b773f35 --- /dev/null +++ b/packages/preview/smartart/0.1.0/stairs.typ @@ -0,0 +1,29 @@ +// Stairs: ascending step-up process. Import with: #import "stairs.typ": stairs +// +// #stairs(([Plan], [Develop], ([Launch], rgb("#e64553")))) +#import "@preview/cetz:0.5.2": canvas, draw +#import "common.typ": palette, _lab, _col + +#let stairs( + items, + w: 3.4, h: 1.7, dx: 3.6, dy: 1.9, + accent: luma(130), + text-fill: white, size: 16pt, radius: 0.15, +) = canvas({ + import draw: * + let n = items.len() + let c = i => (i * dx, i * dy) + for i in range(n - 1) { + let a = c(i) + let b = c(i + 1) + line((a.at(0) + w / 2, a.at(1)), (b.at(0) - w / 2, b.at(1)), + mark: (end: ">", fill: accent), stroke: accent + 3pt) + } + for (i, it) in items.enumerate() { + let p = c(i) + rect((p.at(0) - w / 2, p.at(1) - h / 2), (p.at(0) + w / 2, p.at(1) + h / 2), + fill: _col(it, i), stroke: none, radius: radius) + content(p, box(width: (w - 0.5) * 1cm, + align(center, text(fill: text-fill, weight: "bold", size: size)[#_lab(it)]))) + } +}) diff --git a/packages/preview/smartart/0.1.0/steps.typ b/packages/preview/smartart/0.1.0/steps.typ new file mode 100644 index 0000000000..8d7a2ebdd4 --- /dev/null +++ b/packages/preview/smartart/0.1.0/steps.typ @@ -0,0 +1,25 @@ +// Vertical numbered steps list. Import with: #import "steps.typ": steps +// +// #steps(([Plan], [Execute], ([Review], rgb("#e64553")))) +#import "common.typ": palette, _lab, _col + +#let steps( + items, + accent: rgb("#73c25a"), + box-fill: rgb("#ebedf5"), + text-fill: rgb("#1f1f3a"), + size: 18pt, + radius: 8pt, + gutter: 10pt, + inset: 14pt, +) = { + set text(size: size, fill: text-fill) + set par(justify: false, leading: 0.5em) + stack(dir: ttb, spacing: gutter, ..items.enumerate().map(((i, it)) => { + let fill = if type(it) == array and it.len() > 1 { it.at(1) } else { box-fill } + grid(columns: (auto, 1fr), column-gutter: gutter, align: (horizon, horizon), + box(width: 1.6em, height: 1.6em, fill: accent, radius: 50%, + align(center + horizon, text(fill: white, weight: "bold")[#(i + 1)])), + box(width: 100%, fill: fill, radius: radius, inset: inset, _lab(it))) + })) +} diff --git a/packages/preview/smartart/0.1.0/table.typ b/packages/preview/smartart/0.1.0/table.typ new file mode 100644 index 0000000000..10efb07a01 --- /dev/null +++ b/packages/preview/smartart/0.1.0/table.typ @@ -0,0 +1,86 @@ +// Reusable "card" table — rounded cells with white gaps, lavender header, +// alternating pink rows, centred text. Import with: #import "table.typ": card-table +// +// #card-table( +// ("Study", "Behavior used", "Approach", "Environment", "Dataset", "Indicators"), +// ( +// ([Guo et al. (2023)], [Arm movements...], [Propose two...], [Mobile devices], [Homemade], [CDF, ...]), +// ([Gurunathan (2024)], [PPG], [Exhaustive...], [Not specified], [PUT Vein], [FRR, ...]), +// ), +// ) +// +// Every cell in a row gets the same height (= tallest cell), so the cards line +// up. `weights` sets relative column widths (default: all equal). + +// Uso: #card-row((celda1, celda2, ...), fill: red, text-fill: white) +// Los parámetros `fill` y `text-fill` son opcionales. +#let card-row(cells, fill: none, text-fill: none) = ( + _card-row: true, + cells: cells, + fill: fill, + text-fill: text-fill, +) + +#let card-table( + headers, + rows, + weights: none, + header-fill: rgb("#aab4f7"), + row-fills: (rgb("#f5e6e2"), rgb("#ecd0d2")), + text-fill: rgb("#1f1f3a"), + header-text-fill: auto, + size: 18pt, + radius: 12pt, + gutter: 8pt, + inset: 12pt, +) = layout(area => { + let n = headers.len() + let w = if weights == none { range(n).map(_ => 1) } else { weights } + let total = w.sum() + let avail = area.width - gutter * (n - 1) + let colw = w.map(x => avail * x / total) + + let head-c = if header-text-fill == auto { text-fill } else { header-text-fill } + set text(size: size, fill: text-fill) + set par(justify: false, leading: 0.5em) + + // Helper: extrae cells, fill y text-fill de una fila (soporta card-row o tupla) + let parse-row(r, i) = { + if type(r) == dictionary and r.at("_card-row", default: false) == true { + let fill = if r.fill != none { r.fill } else { row-fills.at(calc.rem(i, row-fills.len())) } + let tcol = if r.text-fill != none { r.text-fill } else { text-fill } + (cells: r.cells, fill: fill, tcol: tcol) + } else { + (cells: r, fill: row-fills.at(calc.rem(i, row-fills.len())), tcol: text-fill) + } + } + + let row-block(cells, fill, tcol, bold: false) = context { + let hs = cells.enumerate().map(((i, c)) => measure(box(width: colw.at(i) - 2 * inset, c)).height) + let row-h = calc.max(..hs) + 2 * inset + + stack(dir: ltr, spacing: gutter, ..cells + .enumerate() + .map(((i, c)) => box( + width: colw.at(i), + height: row-h, + fill: fill, + radius: radius, + inset: inset, + align( + center + horizon, + text(fill: tcol, if bold { strong(c) } else { c }), + ), + ))) + } + + // Procesamos las filas con parse-row + let parsed-rows = rows + .enumerate() + .map(((i, r)) => { + let p = parse-row(r, i) + row-block(p.cells, p.fill, p.tcol) + }) + + stack(dir: ttb, spacing: gutter, row-block(headers, header-fill, head-c, bold: true), ..parsed-rows) +}) diff --git a/packages/preview/smartart/0.1.0/target.typ b/packages/preview/smartart/0.1.0/target.typ new file mode 100644 index 0000000000..5e0a0f7fb5 --- /dev/null +++ b/packages/preview/smartart/0.1.0/target.typ @@ -0,0 +1,26 @@ +// Target: concentric rings, outermost first. Import with: #import "target.typ": target +// +// #target(([Vision], [Mission], ([Goal], rgb("#e64553")))) +#import "@preview/cetz:0.5.2": canvas, draw +#import "common.typ": palette, _lab, _col + +#let target( + items, + radius: 5, + text-fill: white, + size: 16pt, +) = canvas({ + import draw: * + let n = items.len() + for (i, it) in items.enumerate() { + circle((0, 0), radius: radius * (n - i) / n, fill: _col(it, i), stroke: white + 2pt) + } + for (i, it) in items.enumerate() { + let ro = radius * (n - i) / n + let ri = radius * (n - i - 1) / n + let y = if i == n - 1 { 0 } else { (ro + ri) / 2 } + let w = calc.max(1.0, 2 * calc.sqrt(calc.max(0.01, ro * ro - y * y)) - 0.4) + content((0, y), box(width: w * 1cm, + align(center, text(fill: text-fill, weight: "bold", size: size)[#_lab(it)]))) + } +}) diff --git a/packages/preview/smartart/0.1.0/timeline.typ b/packages/preview/smartart/0.1.0/timeline.typ new file mode 100644 index 0000000000..c2547a7d2e --- /dev/null +++ b/packages/preview/smartart/0.1.0/timeline.typ @@ -0,0 +1,29 @@ +// Timeline: milestones along a horizontal line. Import with: #import "timeline.typ": timeline +// +// #timeline((("2024", [Start]), ("2025", [Pilot]), ("2026", [Launch]))) +#import "@preview/cetz:0.5.2": canvas, draw +#import "common.typ": palette, _lab, _col + +#let timeline( + items, + accent: rgb("#73c25a"), + text-fill: rgb("#1f1f3a"), + size: 16pt, date-size: 14pt, step: 5, +) = canvas({ + import draw: * + let n = items.len() + let x = i => i * step + line((-step * 0.5, 0), ((n - 1) * step + step * 0.6, 0), + mark: (end: ">", fill: accent), stroke: accent + 4pt) + for (i, it) in items.enumerate() { + let xi = x(i) + let col = it.at(2, default: palette.at(calc.rem(i, palette.len()))) + let up = calc.rem(i, 2) == 0 + let s = if up { 1 } else { -1 } + line((xi, 0), (xi, s * 0.9), stroke: col + 2pt) + circle((xi, 0), radius: 0.28, fill: col, stroke: white + 2pt) + content((xi, s * 1.25), text(fill: accent, weight: "bold", size: date-size)[#it.at(0)]) + content((xi, s * 2.05), box(width: (step + 0.4) * 1cm, + align(center, text(fill: text-fill, weight: "bold", size: size)[#it.at(1)]))) + } +}) diff --git a/packages/preview/smartart/0.1.0/tree.typ b/packages/preview/smartart/0.1.0/tree.typ new file mode 100644 index 0000000000..3a6920f176 --- /dev/null +++ b/packages/preview/smartart/0.1.0/tree.typ @@ -0,0 +1,78 @@ +// Tree: multi-level hierarchy. Import with: #import "tree.typ": tree +// +// #tree(([CEO], (([Eng], ([Web], [Mobile])), [Sales]))) +// #tree((([CEO], blue), ((([Eng], green), (([Web], orange), ([Mobile], red))), [Sales]))) +#import "@preview/cetz:0.5.2": canvas, draw + +#let _tlab(nd) = { + if type(nd) != array { return nd } + let first = nd.at(0) + if type(first) == array { return first.at(0) } + first +} +#let _tcol(nd) = { + if type(nd) != array { return none } + let first = nd.at(0) + if type(first) == array and first.len() >= 2 and type(first.at(1)) == color { return first.at(1) } + if nd.len() >= 2 and type(nd.at(1)) == color { return nd.at(1) } + none +} +#let _tkids(nd) = { + if type(nd) != array { return () } + let second = nd.at(1, default: ()) + if type(second) == color { return () } + second +} +#let _tlayout(nd, depth, x0) = { + let kids = _tkids(nd) + if kids.len() == 0 { + (((x: x0, depth: depth, label: _tlab(nd), kids: (), color: _tcol(nd)),), x0 + 1) + } else { + let acc = () + let cx = x0 + let roots = () + for k in kids { + let res = _tlayout(k, depth + 1, cx) + cx = res.at(1) + roots.push(res.at(0).first().x) + acc += res.at(0) + } + (((x: (roots.first() + roots.last()) / 2, depth: depth, label: _tlab(nd), kids: roots, color: _tcol(nd)),) + acc, cx) + } +} +#let tree( + root, + box-w: 4, box-h: 1.5, xstep: 4.6, ystep: 2.8, + root-fill: rgb("#aab4f7"), box-fill: rgb("#a7dd9b"), + text-fill: rgb("#1f1f3a"), accent: rgb("#73c25a"), size: 15pt, + filled: true, + outline: 2.5pt, +) = canvas({ + import draw: * + let nodes = _tlayout(root, 0, 0).at(0) + for nd in nodes { + let px = nd.x * xstep + let py = -nd.depth * ystep + for kx in nd.kids { + let cxp = kx * xstep + let cyp = -(nd.depth + 1) * ystep + let midy = (py - box-h / 2 + cyp + box-h / 2) / 2 + line((px, py - box-h / 2), (px, midy), (cxp, midy), (cxp, cyp + box-h / 2), + stroke: (paint: accent, thickness: 2.5pt, join: "round")) + } + } + for nd in nodes { + let px = nd.x * xstep + let py = -nd.depth * ystep + let col = if nd.color != none { nd.color } else { if nd.depth == 0 { root-fill } else { box-fill } } + rect((px - box-w / 2, py - box-h / 2), (px + box-w / 2, py + box-h / 2), + fill: if filled { col } else { none }, + stroke: if filled { none } else { outline + col }, + radius: 0.15) + content((px, py), box(width: (box-w - 0.4) * 1cm, + align(center + horizon, text(fill: text-fill, size: size, [ + #set par(justify: false) + #nd.label + ])))) + } +}) diff --git a/packages/preview/smartart/0.1.0/typst.toml b/packages/preview/smartart/0.1.0/typst.toml new file mode 100644 index 0000000000..a4ec9469fa --- /dev/null +++ b/packages/preview/smartart/0.1.0/typst.toml @@ -0,0 +1,11 @@ +[package] +name = "smartart" +version = "0.1.0" +entrypoint = "smartart.typ" +authors = ["Crag"] +license = "GPL-2.0-only" +description = "SmartArt-style diagrams for posters and presentations." +repository = "https://github.com/CRAG666/smartart" +keywords = ["diagram", "smartart", "poster", "flowchart", "process", "venn", "gantt", "timeline", "cycle", "pyramid", "hierarchy", "matrix", "tree"] +categories = ["visualization", "poster"] +exclude = ["images/*.png"] diff --git a/packages/preview/smartart/0.1.0/venn.typ b/packages/preview/smartart/0.1.0/venn.typ new file mode 100644 index 0000000000..4bb357719a --- /dev/null +++ b/packages/preview/smartart/0.1.0/venn.typ @@ -0,0 +1,28 @@ +// Venn diagram (2 or 3 overlapping circles). Import with: #import "venn.typ": venn +// +// #venn((([Behavior], rgb("#aab4f7")), [Signal], [Security])) +#import "@preview/cetz:0.5.2": canvas, draw +#import "common.typ": palette, _lab, _col + +#let venn( + items, + radius: 3.4, + text-fill: rgb("#1f1f3a"), + size: 20pt, + transparency: 45%, +) = canvas({ + import draw: * + let n = items.len() + let r = radius + let centers = if n <= 2 { ((-r * 0.6, 0), (r * 0.6, 0)).slice(0, n) } + else { ((0, r * 0.62), (-r * 0.66, -r * 0.5), (r * 0.66, -r * 0.5)) } + let labpos = if n <= 2 { ((-r, 0), (r, 0)).slice(0, n) } + else { ((0, r * 1.35), (-r * 1.15, -r * 0.95), (r * 1.15, -r * 0.95)) } + for (i, it) in items.enumerate() { + let col = _col(it, i) + circle(centers.at(i), radius: r, fill: col.transparentize(transparency), stroke: col + 2pt) + } + for (i, it) in items.enumerate() { + content(labpos.at(i), text(fill: text-fill, weight: "bold", size: size)[#_lab(it)]) + } +})