Repository: mitchmindtree/elmesque
Branch: master
Commit: aef4ec6b478b
Files: 14
Total size: 83.4 KB
Directory structure:
gitextract_r6tt53c7/
├── .gitignore
├── .travis.yml
├── Cargo.toml
├── ELM_LICENSE.md
├── README.md
├── assets/
│ └── NotoSans/
│ └── LICENSE-2.0.txt
├── examples/
│ └── graphics.rs
└── src/
├── color.rs
├── element.rs
├── form.rs
├── lib.rs
├── text.rs
├── transform_2d.rs
└── utils.rs
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# RUST STUFF
# Compiled files
*.o
*.so
*.rlib
*.dll
# Executables
*.exe
# Generated by Cargo
/target/
Cargo.lock
# MAC STUFF
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear on external disk
.Spotlight-V100
.Trashes
================================================
FILE: .travis.yml
================================================
sudo: false
language: rust
os:
- linux
env:
global:
- secure: W/pxVmgtzNXIQNPOm9lsIjSr2nEHGVD8uOGV0be4kdz0bUXCjFDe1j45VVDnXPoJZDrnv7TO0etn3yT7hpuiZGAT40Ovn7LVq7gqtTAoP2U7vbURN55g0MU9dSIAOUdfclAMZez9HgOHWC0P3Tg6bNkNrW5B5wwpmaFVyYwiQkE=
- secure: qlflwsinhvNorlh6l4Hl3tQDytF/LTzlUmw3hA4yj7pwEFUP4BORTvNIlJa+DCft4P4aEU0pgCsC8eb+MQ+q1WOQr2e+EfE+KT/FS9pT6RvqyYUs4QaEznbJkHxMjzkU2N5jf6RGssIEx3ieXD1y+LETxk+KIBFY8DN+wmMYjas=
addons:
apt:
packages:
- libxxf86vm-dev
- libosmesa6-dev
script:
- cargo build --verbose
- cargo test --verbose
- cargo doc --verbose
after_success: |
[ $TRAVIS_BRANCH = master ] &&
[ $TRAVIS_PULL_REQUEST = false ] &&
cargo doc &&
echo "<meta http-equiv=refresh content=0;url=`echo $TRAVIS_REPO_SLUG | cut -d '/' -f 2`/index.html>" > target/doc/index.html &&
sudo pip install ghp-import &&
ghp-import -n target/doc &&
git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages
================================================
FILE: Cargo.toml
================================================
[package]
name = "elmesque"
version = "0.12.0"
authors = ["mitchmindtree <mitchell.nordine@gmail.com>"]
description = "An attempt at porting Elm's incredibly useful, purely functional std graphics modules."
readme = "README.md"
keywords = ["elm", "graphics", "2d", "ui", "shape"]
license = "MIT"
repository = "https://github.com/mitchmindtree/elmesque.git"
homepage = "https://github.com/mitchmindtree/elmesque"
[dependencies]
num = "0.1.27"
piston2d-graphics = "0.13.0"
rand = "0.3.12"
rustc-serialize = "0.3.16"
vecmath = "0.2.0"
[dev-dependencies]
find_folder = "0.3.0"
piston = "0.16.0"
piston_window = "0.33.0"
================================================
FILE: ELM_LICENSE.md
================================================
Copyright (c) 2013-2015, Evan Czaplicki
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Evan Czaplicki nor the names of other
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: README.md
================================================
# elmesque [](https://travis-ci.org/mitchmindtree/elmesque)
This crate is an attempt at porting Elm's incredibly useful, purely functional std graphics modules. Its useful for all kinds of 2D freeform graphics and UI design.
See [the docs](http://mitchmindtree.github.io/elmesque) or checkout [the example](https://github.com/mitchmindtree/elmesque/blob/master/examples/graphics.rs).
Visit [elm-lang.org](http://elm-lang.org/) to learn more about Elm.
All credit and thanks goes to Evan Czaplicki for all algorithms included within.
Ported to Rust by Mitchell Nordine.
Usage
-----
Add elmesque to your cargo dependencies like so.
```toml
[dependencies]
elmesque = "*"
```
================================================
FILE: assets/NotoSans/LICENSE-2.0.txt
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: examples/graphics.rs
================================================
extern crate elmesque;
extern crate find_folder;
extern crate graphics;
extern crate num;
extern crate piston;
extern crate piston_window;
use elmesque::{Form, Renderer};
use piston::input::UpdateEvent;
use piston::window::WindowSettings;
use piston_window::{PistonWindow, Glyphs};
fn main() {
// Construct the window.
let window: PistonWindow =
WindowSettings::new("Elmesque", [1180, 580])
.exit_on_esc(true)
.samples(4)
.vsync(true)
.build()
.unwrap();
// Construct the GlyphCache.
let mut glyph_cache = {
let assets = find_folder::Search::ParentsThenKids(3, 3).for_folder("assets").unwrap();
let font_path = assets.join("NotoSans/NotoSans-Regular.ttf");
Glyphs::new(&font_path, window.factory.borrow().clone()).unwrap()
};
// We'll use this to animate our graphics.
let mut secs = 0.0;
// Poll events from the window.
for event in window {
event.draw_2d(|context, g| {
let view_dim = context.get_view_size();
let (w, h) = (view_dim[0], view_dim[1]);
// Construct the elmesque Renderer with our graphics backend and glyph cache.
let mut renderer = Renderer::new(context, g).character_cache(&mut glyph_cache);
// Construct some freeform graphics aka a `Form`.
let form = elmesque_demo_form(secs);
// Convert the form to an `Element` for rendering.
let a = elmesque::form::collage(w as i32, h as i32, vec![form])
//.crop((secs / 2.0).sin() * (w / 2.0), (secs / 3.0).sin() * (h / 2.0), 400.0, 400.0)
.clear(elmesque::color::black());
a.draw(&mut renderer);
});
event.update(|args| secs += args.dt);
}
}
/// Demo of grouping multiple forms into a new single form, transformable at any stage.
pub fn elmesque_demo_form(secs: f64) -> Form {
use elmesque::color::{blue, dark_blue, light_blue, dark_purple, white};
use elmesque::form::{circle, group, ngon, oval, point_path, rect, solid, text, traced};
use elmesque::text::Text;
use elmesque::utils::{degrees};
use num::Float;
// Time to get creative!
group(vec![
rect(60.0, 40.0).filled(blue())
.shift(secs.sin() * 50.0, secs.cos() * 50.0)
.alpha(((secs * 200.0).cos() * 0.5 + 0.5) as f32)
.rotate(-secs),
rect(100.0, 10.0).filled(dark_blue())
.shift((secs * 5.0).sin() * 200.0, (secs * 5.0).cos() * 200.0)
.alpha(((secs * 2.0).cos() * 0.5 + 0.5) as f32)
.rotate(-(secs * 5.0)),
rect(10.0, 300.0).filled(blue())
.alpha(((secs * 3.0).sin() * 0.25 + 0.75) as f32)
.rotate(-(secs * 1.5)),
rect(5.0, (secs * 0.1).sin() * 600.0 + 300.0).filled(light_blue())
.alpha(((secs).cos() * 0.25 + 0.75) as f32)
.rotate(secs * 0.75),
rect(3.0, 2000.0).filled(dark_blue())
.alpha(((secs * 100.0).cos() * 0.5 + 0.25) as f32)
.rotate(-(secs * 0.5)),
oval(3.0, 2000.0 * (secs * 60.0).sin()).filled(light_blue())
.alpha(((secs * 100.0).cos() * 0.5 + 0.25) as f32)
.rotate(-(secs * 0.6)),
rect(10.0, 750.0).filled(blue())
.alpha(((secs * 2.0).cos() * 0.5 + 0.25) as f32)
.rotate(-(secs * 1.85)),
circle((secs * 0.5).sin() * 1500.0).outlined(solid(dark_purple()))
.alpha(((secs * 0.2).sin() * 0.25 + 0.35) as f32)
.rotate(-(secs * 0.5)),
ngon(12, (secs * 0.1).cos() * 100.0 + 300.0).filled(blue())
.alpha((0.25 * secs.cos()) as f32)
.rotate(secs * 0.5),
ngon(9, (secs * 0.1).cos() * 200.0 + 250.0).outlined(solid(dark_blue()))
.alpha(((0.33 * secs).sin() + 0.15) as f32)
.rotate(secs * 0.2),
rect(300.0, 20.0).filled(light_blue())
.shift((secs * 1.5).cos() * 250.0, (secs * 1.5).sin() * 250.0)
.alpha(((secs * 4.5).cos() * 0.25 + 0.35) as f32)
.rotate(secs * 1.5 + degrees(90.0)),
traced(
solid(light_blue()),
point_path(vec![(-500.0, 100.0), (0.0, 250.0 * secs.sin()), (500.0, 100.0)])
).alpha(((secs * 0.2).sin() * 0.25 + 0.35) as f32),
traced(
solid(blue()),
point_path(vec![(-500.0, 0.0), (0.0, 0.0), (500.0, 0.0)])
).alpha(((secs * 4.5).cos() * 0.25 + 0.35) as f32),
traced(
solid(dark_blue()),
point_path(vec![(-500.0, -100.0), (0.0, -250.0 * secs.sin()), (500.0, -100.0)])
).alpha(((secs * 0.15).cos() * 0.25 + 0.35) as f32),
text(Text::from_string("elmesque".to_string()).color(white())),
]).rotate(degrees(secs.sin() * 360.0))
.scale((secs * 0.05).cos() * 0.2 + 0.9)
}
================================================
FILE: src/color.rs
================================================
//!
//! A library providing simple `Color` and `Gradient` types along with useful transformations and
//! presets.
//!
//!
//! Inspiration taken from [elm-lang's color module]
//! (https://github.com/elm-lang/core/blob/62b22218c42fb8ccc996c86bea450a14991ab815/src/Color.elm)
//!
//!
//! Module for working with colors. Includes [RGB](https://en.wikipedia.org/wiki/RGB_color_model)
//! and [HSL](http://en.wikipedia.org/wiki/HSL_and_HSV) creation, gradients and built-in names.
//!
use rustc_serialize::hex::ToHex;
use std::ascii::AsciiExt;
use std::f32::consts::PI;
use utils::{clampf32, degrees, fmod, min, max, turns};
/// Color supporting RGB and HSL variants.
#[derive(PartialEq, Copy, Clone, Debug, RustcEncodable, RustcDecodable)]
pub enum Color {
/// Red, Green, Blue, Alpha - All values' scales represented between 0.0 and 1.0.
Rgba(f32, f32, f32, f32),
/// Hue, Saturation, Lightness, Alpha - all valuess scales represented between 0.0 and 1.0.
Hsla(f32, f32, f32, f32),
}
/// Regional spelling alias.
pub type Colour = Color;
/// Create RGB colors with an alpha component for transparency.
/// The alpha component is specified with numbers between 0 and 1.
#[inline]
pub fn rgba(r: f32, g: f32, b: f32, a: f32) -> Color {
Color::Rgba(r, g, b, a)
}
/// Create RGB colors from numbers between 0.0 and 1.0.
#[inline]
pub fn rgb(r: f32, g: f32, b: f32) -> Color {
Color::Rgba(r, g, b, 1.0)
}
/// Create RGB colors from numbers between 0 and 255 inclusive.
/// The alpha component is specified with numbers between 0 and 1.
#[inline]
pub fn rgba_bytes(r: u8, g: u8, b: u8, a: f32) -> Color {
Color::Rgba(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0, a)
}
/// Create RGB colors from numbers between 0 and 255 inclusive.
#[inline]
pub fn rgb_bytes(r: u8, g: u8, b: u8) -> Color {
rgba_bytes(r, g, b, 1.0)
}
/// Create [HSL colors](http://en.wikipedia.org/wiki/HSL_and_HSV) with an alpha component for
/// transparency.
#[inline]
pub fn hsla(hue: f32, saturation: f32, lightness: f32, alpha: f32) -> Color {
Color::Hsla(hue - turns((hue / (2.0 * PI)).floor()), saturation, lightness, alpha)
}
/// Create [HSL colors](http://en.wikipedia.org/wiki/HSL_and_HSV). This gives you access to colors
/// more like a color wheel, where all hues are arranged in a circle that you specify with radians.
///
/// red = hsl(degrees(0.0) , 1.0 , 0.5)
/// green = hsl(degrees(120.0) , 1.0 , 0.5)
/// blue = hsl(degrees(240.0) , 1.0 , 0.5)
/// pastel_red = hsl(degrees(0.0) , 0.7 , 0.7)
///
/// To cycle through all colors, just cycle through degrees. The saturation level is how vibrant
/// the color is, like a dial between grey and bright colors. The lightness level is a dial between
/// white and black.
#[inline]
pub fn hsl(hue: f32, saturation: f32, lightness: f32) -> Color {
hsla(hue, saturation, lightness, 1.0)
}
/// Produce a gray based on the input. 0.0 is white, 1.0 is black.
pub fn grayscale(p: f32) -> Color {
Color::Hsla(0.0, 0.0, 1.0-p, 1.0)
}
/// Produce a gray based on the input. 0.0 is white, 1.0 is black.
pub fn greyscale(p: f32) -> Color {
Color::Hsla(0.0, 0.0, 1.0-p, 1.0)
}
/// Construct a random color.
pub fn random() -> Color {
rgb(::rand::random(), ::rand::random(), ::rand::random())
}
impl Color {
/// Produce a complementary color. The two colors will accent each other. This is the same as
/// rotating the hue by 180 degrees.
pub fn complement(self) -> Color {
match self {
Color::Hsla(h, s, l, a) => hsla(h + degrees(180.0), s, l, a),
Color::Rgba(r, g, b, a) => {
let (h, s, l) = rgb_to_hsl(r, g, b);
hsla(h + degrees(180.0), s, l, a)
},
}
}
/// Calculate and return the luminance of the Color.
pub fn luminance(&self) -> f32 {
match *self {
Color::Rgba(r, g, b, _) => (r + g + b) / 3.0,
Color::Hsla(_, _, l, _) => l,
}
}
/// Return either black or white, depending which contrasts the Color the most. This will be
/// useful for determining a readable color for text on any given background Color.
pub fn plain_contrast(self) -> Color {
if self.luminance() > 0.5 { black() } else { white() }
}
/// Extract the components of a color in the HSL format.
pub fn to_hsl(self) -> Hsla {
match self {
Color::Hsla(h, s, l, a) => Hsla(h, s, l, a),
Color::Rgba(r, g, b, a) => {
let (h, s, l) = rgb_to_hsl(r, g, b);
Hsla(h, s, l, a)
},
}
}
/// Extract the components of a color in the RGB format.
pub fn to_rgb(self) -> Rgba {
match self {
Color::Rgba(r, g, b, a) => Rgba(r, g, b, a),
Color::Hsla(h, s, l, a) => {
let (r, g, b) = hsl_to_rgb(h, s, l);
Rgba(r, g, b, a)
},
}
}
/// Extract the components of a color in the RGB format within a fixed-size array.
pub fn to_fsa(self) -> [f32; 4] {
let Rgba(r, g, b, a) = self.to_rgb();
[r, g, b, a]
}
/// Same as `to_fsa`, except r, g, b and a are represented in byte form.
pub fn to_byte_fsa(self) -> [u8; 4] {
let Rgba(r, g, b, a) = self.to_rgb();
[f32_to_byte(r), f32_to_byte(g), f32_to_byte(b), f32_to_byte(a)]
}
/// Return the hex representation of this color in the format #RRGGBBAA
/// e.g. `Color(1.0, 0.0, 5.0, 1.0) == "#FF0080FF"`
pub fn to_hex(self) -> String {
let vals = self.to_byte_fsa();
let hex = vals.to_hex().to_ascii_uppercase();
format!("#{}", &hex)
}
/// Return the same color but with the given luminance.
pub fn with_luminance(self, l: f32) -> Color {
let Hsla(h, s, _, a) = self.to_hsl();
Color::Hsla(h, s, l, a)
}
/// Return the same color but with the alpha multiplied by the given alpha.
pub fn alpha(self, alpha: f32) -> Color {
match self {
Color::Rgba(r, g, b, a) => Color::Rgba(r, g, b, a * alpha),
Color::Hsla(h, s, l, a) => Color::Hsla(h, s, l, a * alpha),
}
}
/// Return the same color but with the given alpha.
pub fn with_alpha(self, a: f32) -> Color {
match self {
Color::Rgba(r, g, b, _) => Color::Rgba(r, g, b, a),
Color::Hsla(h, s, l, _) => Color::Hsla(h, s, l, a),
}
}
/// Return a highlighted version of the current Color.
pub fn highlighted(self) -> Color {
let luminance = self.luminance();
let Rgba(r, g, b, a) = self.to_rgb();
let (r, g, b) = {
if luminance > 0.8 { (r - 0.2, g - 0.2, b - 0.2) }
else if luminance < 0.2 { (r + 0.2, g + 0.2, b + 0.2) }
else {
(clampf32((1.0 - r) * 0.5 * r + r),
clampf32((1.0 - g) * 0.1 * g + g),
clampf32((1.0 - b) * 0.1 * b + b))
}
};
let a = clampf32((1.0 - a) * 0.5 + a);
rgba(r, g, b, a)
}
/// Return a clicked version of the current Color.
pub fn clicked(&self) -> Color {
let luminance = self.luminance();
let Rgba(r, g, b, a) = self.to_rgb();
let (r, g, b) = {
if luminance > 0.8 { (r , g - 0.2, b - 0.2) }
else if luminance < 0.2 { (r + 0.4, g + 0.2, b + 0.2) }
else {
(clampf32((1.0 - r) * 0.75 + r),
clampf32((1.0 - g) * 0.25 + g),
clampf32((1.0 - b) * 0.25 + b))
}
};
let a = clampf32((1.0 - a) * 0.75 + a);
rgba(r, g, b, a)
}
/// Return the Color's invert.
pub fn invert(self) -> Color {
let Rgba(r, g, b, a) = self.to_rgb();
rgba((r - 1.0).abs(), (g - 1.0).abs(), (b - 1.0).abs(), a)
}
/// Return the red value.
pub fn red(&self) -> f32 {
let Rgba(r, _, _, _) = self.to_rgb();
r
}
/// Return the green value.
pub fn green(&self) -> f32 {
let Rgba(_, g, _, _) = self.to_rgb();
g
}
/// Return the blue value.
pub fn blue(&self) -> f32 {
let Rgba(_, _, b, _) = self.to_rgb();
b
}
/// Set the red value.
pub fn set_red(&mut self, r: f32) {
let Rgba(_, g, b, a) = self.to_rgb();
*self = rgba(r, g, b, a);
}
/// Set the green value.
pub fn set_green(&mut self, g: f32) {
let Rgba(r, _, b, a) = self.to_rgb();
*self = rgba(r, g, b, a);
}
/// Set the blue value.
pub fn set_blue(&mut self, b: f32) {
let Rgba(r, g, _, a) = self.to_rgb();
*self = rgba(r, g, b, a);
}
}
/// The parts of HSL along with an alpha for transparency.
#[derive(Copy, Clone, Debug)]
pub struct Hsla(pub f32, pub f32, pub f32, pub f32);
/// The parts of RGB along with an alpha for transparency.
#[derive(Copy, Clone, Debug)]
pub struct Rgba(pub f32, pub f32, pub f32, pub f32);
/// Convert an f32 color to a byte.
#[inline]
pub fn f32_to_byte(c: f32) -> u8 { (c * 255.0) as u8 }
/// Pure function for converting rgb to hsl.
pub fn rgb_to_hsl(r: f32, g: f32, b: f32) -> (f32, f32, f32) {
let c_max = max(max(r, g), b);
let c_min = min(min(r, g), b);
let c = c_max - c_min;
let hue = if c == 0.0 {
// If there's no difference in the channels we have grayscale, so the hue is undefined.
0.0
} else {
degrees(60.0) * if c_max == r { fmod(((g - b) / c), 6) }
else if c_max == g { ((b - r) / c) + 2.0 }
else { ((r - g) / c) + 4.0 }
};
let lightness = (c_max + c_min) / 2.0;
let saturation = if lightness == 0.0 { 0.0 }
else { c / (1.0 - (2.0 * lightness - 1.0).abs()) };
(hue, saturation, lightness)
}
/// Pure function for converting hsl to rgb.
pub fn hsl_to_rgb(hue: f32, saturation: f32, lightness: f32) -> (f32, f32, f32) {
let chroma = (1.0 - (2.0 * lightness - 1.0).abs()) * saturation;
let hue = hue / degrees(60.0);
let x = chroma * (1.0 - (fmod(hue, 2) - 1.0).abs());
let (r, g, b) = match hue {
hue if hue < 0.0 => (0.0, 0.0, 0.0),
hue if hue < 1.0 => (chroma, x, 0.0),
hue if hue < 2.0 => (x, chroma, 0.0),
hue if hue < 3.0 => (0.0, chroma, x),
hue if hue < 4.0 => (0.0, x, chroma),
hue if hue < 5.0 => (x, 0.0, chroma),
hue if hue < 6.0 => (chroma, 0.0, x),
_ => (0.0, 0.0, 0.0),
};
let m = lightness - chroma / 2.0;
(r + m, g + m, b + m)
}
/// Linear or Radial Gradient.
#[derive(Clone, Debug)]
pub enum Gradient {
/// Takes a start and end point and then a series of color stops that indicate how to
/// interpolate between the start and end points.
Linear((f64, f64), (f64, f64), Vec<(f64, Color)>),
/// First takes a start point and inner radius. Then takes an end point and outer radius.
/// It then takes a series of color stops that indicate how to interpolate between the
/// inner and outer circles.
Radial((f64, f64), f64, (f64, f64), f64, Vec<(f64, Color)>),
}
/// Create a linear gradient.
pub fn linear(start: (f64, f64), end: (f64, f64), colors: Vec<(f64, Color)>) -> Gradient {
Gradient::Linear(start, end, colors)
}
/// Create a radial gradient.
pub fn radial(start: (f64, f64), start_r: f64,
end: (f64, f64), end_r: f64,
colors: Vec<(f64, Color)>) -> Gradient {
Gradient::Radial(start, start_r, end, end_r, colors)
}
/// Built-in colors.
///
/// These colors come from the
/// [Tango palette](http://tango.freedesktop.org/Tango_Icon_Theme_Guidelines) which provides
/// aesthetically reasonable defaults for colors. Each color also comes with a light and dark
/// version.
/// Scarlet Red - Light - #EF2929
pub fn light_red() -> Color { rgb_bytes(239 , 41 , 41 ) }
/// Scarlet Red - Regular - #CC0000
pub fn red() -> Color { rgb_bytes(204 , 0 , 0 ) }
/// Scarlet Red - Dark - #A30000
pub fn dark_red() -> Color { rgb_bytes(164 , 0 , 0 ) }
/// Orange - Light - #FCAF3E
pub fn light_orange() -> Color { rgb_bytes(252 , 175 , 62 ) }
/// Orange - Regular - #F57900
pub fn orange() -> Color { rgb_bytes(245 , 121 , 0 ) }
/// Orange - Dark - #CE5C00
pub fn dark_orange() -> Color { rgb_bytes(206 , 92 , 0 ) }
/// Butter - Light - #FCE94F
pub fn light_yellow() -> Color { rgb_bytes(255 , 233 , 79 ) }
/// Butter - Regular - #EDD400
pub fn yellow() -> Color { rgb_bytes(237 , 212 , 0 ) }
/// Butter - Dark - #C4A000
pub fn dark_yellow() -> Color { rgb_bytes(196 , 160 , 0 ) }
/// Chameleon - Light - #8AE234
pub fn light_green() -> Color { rgb_bytes(138 , 226 , 52 ) }
/// Chameleon - Regular - #73D216
pub fn green() -> Color { rgb_bytes(115 , 210 , 22 ) }
/// Chameleon - Dark - #4E9A06
pub fn dark_green() -> Color { rgb_bytes(78 , 154 , 6 ) }
/// Sky Blue - Light - #729FCF
pub fn light_blue() -> Color { rgb_bytes(114 , 159 , 207) }
/// Sky Blue - Regular - #3465A4
pub fn blue() -> Color { rgb_bytes(52 , 101 , 164) }
/// Sky Blue - Dark - #204A87
pub fn dark_blue() -> Color { rgb_bytes(32 , 74 , 135) }
/// Plum - Light - #AD7FA8
pub fn light_purple() -> Color { rgb_bytes(173 , 127 , 168) }
/// Plum - Regular - #75507B
pub fn purple() -> Color { rgb_bytes(117 , 80 , 123) }
/// Plum - Dark - #5C3566
pub fn dark_purple() -> Color { rgb_bytes(92 , 53 , 102) }
/// Chocolate - Light - #E9B96E
pub fn light_brown() -> Color { rgb_bytes(233 , 185 , 110) }
/// Chocolate - Regular - #C17D11
pub fn brown() -> Color { rgb_bytes(193 , 125 , 17 ) }
/// Chocolate - Dark - #8F5902
pub fn dark_brown() -> Color { rgb_bytes(143 , 89 , 2 ) }
/// Straight Black.
pub fn black() -> Color { rgb_bytes(0 , 0 , 0 ) }
/// Straight White.
pub fn white() -> Color { rgb_bytes(255 , 255 , 255) }
/// Alluminium - Light
pub fn light_gray() -> Color { rgb_bytes(238 , 238 , 236) }
/// Alluminium - Regular
pub fn gray() -> Color { rgb_bytes(211 , 215 , 207) }
/// Alluminium - Dark
pub fn dark_gray() -> Color { rgb_bytes(186 , 189 , 182) }
/// Aluminium - Light - #EEEEEC
pub fn light_grey() -> Color { rgb_bytes(238 , 238 , 236) }
/// Aluminium - Regular - #D3D7CF
pub fn grey() -> Color { rgb_bytes(211 , 215 , 207) }
/// Aluminium - Dark - #BABDB6
pub fn dark_grey() -> Color { rgb_bytes(186 , 189 , 182) }
/// Charcoal - Light - #888A85
pub fn light_charcoal() -> Color { rgb_bytes(136 , 138 , 133) }
/// Charcoal - Regular - #555753
pub fn charcoal() -> Color { rgb_bytes(85 , 87 , 83 ) }
/// Charcoal - Dark - #2E3436
pub fn dark_charcoal() -> Color { rgb_bytes(46 , 52 , 54 ) }
/// Types that can be colored.
pub trait Colorable: Sized {
/// Set the color of the widget.
fn color(self, color: Color) -> Self;
/// Set the color of the widget from rgba values.
fn rgba(self, r: f32, g: f32, b: f32, a: f32) -> Self {
self.color(rgba(r, g, b, a))
}
/// Set the color of the widget from rgb values.
fn rgb(self, r: f32, g: f32, b: f32) -> Self {
self.color(rgb(r, g, b))
}
/// Set the color of the widget from hsla values.
fn hsla(self, h: f32, s: f32, l: f32, a: f32) -> Self {
self.color(hsla(h, s, l, a))
}
/// Set the color of the widget from hsl values.
fn hsl(self, h: f32, s: f32, l: f32) -> Self {
self.color(hsl(h, s, l))
}
}
================================================
FILE: src/element.rs
================================================
//!
//! Ported from [elm-lang's `Graphics.Element` module]
//! (https://github.com/elm-lang/core/blob/1.1.1/src/Graphics/Element.elm)
//!
//!
//! Graphical elements that snap together to build complex widgets and layouts.
//!
//! Each element is a rectangle with a known width and height, making them easy to combine and
//! position.
//!
//!
//! # Images
//!
//! image, fitted_image, cropped_image, tiled_image
//!
//!
//! # Styling
//!
//! width, height, size, color, opacity
//!
//!
//! # Inspection
//!
//! width_of, height_of, size_of
//!
//!
//! # Layout
//!
//! flow, up, down, left, right, inward, outward
//!
//! ## Layout Aliases
//!
//! There are some convenience functions for working with `flow` in specific cases:
//!
//! layers, above, below, beside
//!
//!
//! # Positioning
//! empty, spacer, container
//!
//! ## Specific Positions
//!
//! To create a `Position` you can use any of the built-in positions which cover nine common
//! positions:
//!
//! middle, mid_top, mid_bottom, mid_left, mid_right, top_left, top_right, bottom_left,
//! bottom_right
//!
//! If you need more precision, you can create custom positions:
//!
//! absolute, relative, middle_at, mid_top_at, mid_bottom_at, mid_left_at, mid_right_at,
//! top_left_at, top_right_at, bottom_left_at, bottom_right_at
//!
use color::Color;
use form::{self, Form};
use graphics::character::CharacterCache;
use graphics::{Context, Graphics, Transformed};
use self::Three::{P, Z, N};
use std::path::PathBuf;
use transform_2d;
/// An Element's Properties.
#[derive(Clone, Debug)]
pub struct Properties {
pub width: i32,
pub height: i32,
pub opacity: f32,
pub crop: Option<(f64, f64, f64, f64)>,
pub color: Option<Color>,
}
/// Graphical elements that snap together to build complex widgets and layouts.
///
/// Each element is a rectangle with a known width and height, making them easy to combine and
/// position.
#[derive(Clone, Debug)]
pub struct Element {
pub props: Properties,
pub element: Prim,
}
impl Element {
/// Create an `Element` with a given width.
#[inline]
pub fn width(self, new_width: i32) -> Element {
let Element { props, element } = self;
let new_props = match element {
Prim::Image(_, w, h, _) | Prim::Collage(w, h, _) => {
Properties {
height: (h as f32 / w as f32 * new_width as f32).round() as i32,
..props
}
},
_ => props,
};
Element { props: new_props, element: element }
}
/// Create an `Element` with a given height.
#[inline]
pub fn height(self, new_height: i32) -> Element {
let Element { props, element } = self;
let new_props = match element {
Prim::Image(_, w, h, _) | Prim::Collage(w, h, _) => {
Properties {
width: (w as f32 / h as f32 * new_height as f32).round() as i32,
..props
}
},
_ => props,
};
Element { props: new_props, element: element }
}
/// Create an `Element` with a given size.
#[inline]
pub fn size(self, new_w: i32, new_h: i32) -> Element {
self.height(new_h).width(new_w)
}
/// Create an `Element` with a given opacity.
#[inline]
pub fn opacity(mut self, opacity: f32) -> Element {
self.props.opacity = opacity;
self
}
/// Create an `Element with a given background color.
#[inline]
pub fn color(mut self, color: Color) -> Element {
self.props.color = Some(color);
self
}
/// Crops an `Element` with the given rectangle.
#[inline]
pub fn crop(self, x: f64, y: f64, w: f64, h: f64) -> Element {
let Element { props, element } = self;
let new_props = Properties { crop: Some((x, y, w, h)), ..props };
Element { props: new_props, element: element }
}
/// Put an element in a container. This lets you position the element really easily, and there are
/// tons of ways to set the `Position`.
#[inline]
pub fn container(self, w: i32, h: i32, pos: Position) -> Element {
new_element(w, h, Prim::Container(pos, Box::new(self)))
}
/// Put an element in a cleared wrapper. The color provided will be the color that clears the
/// screen before rendering the contained element.
#[inline]
pub fn clear(self, color: Color) -> Element {
new_element(self.get_width(), self.get_height(),
Prim::Cleared(color, Box::new(self)))
}
/// Stack elements vertically. To put `a` above `b` you would say: `a.above(b)`
#[inline]
pub fn above(self, other: Element) -> Element {
new_element(::std::cmp::max(self.get_width(), other.get_width()),
self.get_height() + other.get_height(),
Prim::Flow(down(), vec![self, other]))
}
/// Stack elements vertically. To put `a` below `b` you would say: `a.below(b)`
#[inline]
pub fn below(self, other: Element) -> Element {
other.above(self)
}
/// Put elements beside each other horizontally. To put `b` to the right of `a` you would say:
/// `a.beside(b)`
#[inline]
pub fn beside(self, other: Element) -> Element {
new_element(self.get_width() + other.get_width(),
::std::cmp::max(self.get_height(), other.get_height()),
Prim::Flow(right(), vec![self, other]))
}
/// Return the width of the Element.
pub fn get_width(&self) -> i32 { self.props.width }
/// Return the height of the Element.
pub fn get_height(&self) -> i32 { self.props.height }
/// Return the size of the Element's bounding rectangle.
pub fn get_size(&self) -> (i32, i32) { (self.props.width, self.props.height) }
/// Draw the form with some given graphics backend.
#[inline]
pub fn draw<'a, C, G>(&self, renderer: &mut Renderer<'a, C, G>)
where
C: CharacterCache,
G: Graphics<Texture=C::Texture>,
{
let Renderer {
context,
ref mut backend,
ref mut maybe_character_cache,
} = *renderer;
let view_size = context.get_view_size();
let context = context.trans(view_size[0] / 2.0, view_size[1] / 2.0).scale(1.0, -1.0);
draw_element(self, 1.0, *backend, maybe_character_cache, context);
}
/// Return whether or not a point is over the element.
pub fn is_over(&self, x: i32, y: i32) -> bool {
unimplemented!();
}
}
/// Return the size of the Element.
pub fn size_of(e: &Element) -> (i32, i32) {
(e.props.width, e.props.height)
}
/// Construct a new Element from width, height and some Prim.
/// Iterates the global GUID counter by one and returns that as the Element id.
#[inline]
pub fn new_element(w: i32, h: i32, element: Prim) -> Element {
Element {
props: Properties {
width: w,
height: h,
opacity: 1.0,
color: None,
crop: None,
},
element: element,
}
}
/// Create an empty box. this is useful for getting your spacing right and making borders.
pub fn spacer(w: i32, h: i32) -> Element {
new_element(w, h, Prim::Spacer)
}
/// An Element that takes up no space. Good for things that appear conditionally.
pub fn empty() -> Element {
spacer(0, 0)
}
/// The various kinds of Elements.
#[derive(Clone, Debug)]
pub enum Prim {
Image(ImageStyle, i32, i32, PathBuf),
Container(Position, Box<Element>),
Flow(Direction, Vec<Element>),
Collage(i32, i32, Vec<Form>),
Cleared(Color, Box<Element>),
Spacer,
}
/// Styling for the Image Element.
#[derive(Copy, Clone, Debug)]
pub enum ImageStyle {
Plain,
Fitted,
Cropped(i32, i32),
Tiled,
}
/// Create an image given a width, height and texture.
pub fn image(w: i32, h: i32, path: PathBuf) -> Element {
new_element(w, h, Prim::Image(ImageStyle::Plain, w, h, path))
}
/// Create a fitted image given a width, height and texture. This will crop the picture to best
/// fill the given dimensions.
pub fn fitted_image(w: i32, h: i32, path: PathBuf) -> Element {
new_element(w, h, Prim::Image(ImageStyle::Fitted, w, h, path))
}
/// Create a cropped image. Take a rectangle out of the picture starting at the given top left
/// coordinate.
pub fn cropped_image(x: i32, y: i32, w: i32, h: i32, path: PathBuf) -> Element {
new_element(w, h, Prim::Image(ImageStyle::Cropped(x, y), w, h, path))
}
/// Create a tiled image given a width, height and texture.
pub fn tiled_image(w: i32, h: i32, path: PathBuf) -> Element {
new_element(w, h, Prim::Image(ImageStyle::Tiled, w, h, path))
}
#[derive(Copy, Clone, Debug)]
pub enum Three { P, Z, N }
#[derive(Copy, Clone, Debug)]
pub enum Pos { Absolute(i32), Relative(f32) }
/// An element's Position.
#[derive(Copy, Clone, Debug)]
pub struct Position {
horizontal: Three,
vertical: Three,
x: Pos,
y: Pos,
}
/// The direction for a flow of `Element`s.
#[derive(Copy, Clone, Debug)]
pub enum Direction { Up, Down, Left, Right, In, Out }
/// Have a list of elements flow in a particular direction. The `Direction` starts from the first
/// element in the list. The result is an `Element`.
pub fn flow(dir: Direction, elements: Vec<Element>) -> Element {
if elements.is_empty() { return empty() }
let max_w = elements.iter().map(|e| e.get_width()).max().unwrap();
let max_h = elements.iter().map(|e| e.get_height()).max().unwrap();
let sum_w = elements.iter().fold(0, |total, e| total + e.get_width());
let sum_h = elements.iter().fold(0, |total, e| total + e.get_height());
let new_flow = |w: i32, h: i32| new_element(w, h, Prim::Flow(dir, elements));
match dir {
Direction::Up | Direction::Down => new_flow(max_w, sum_h),
Direction::Left | Direction::Right => new_flow(sum_w, max_h),
Direction::In | Direction::Out => new_flow(max_w, max_h),
}
}
/// Layer elements on top of each other, starting from the bottom.
pub fn layers(elements: Vec<Element>) -> Element {
let max_w = elements.iter().map(|e| e.get_width()).max().unwrap_or(0);
let max_h = elements.iter().map(|e| e.get_height()).max().unwrap_or(0);
new_element(max_w, max_h, Prim::Flow(outward(), elements))
}
/// Repetitive things.
pub fn absolute(i: i32) -> Pos { Pos::Absolute(i) }
pub fn relative(f: f32) -> Pos { Pos::Relative(f) }
#[inline]
fn p(h: Three, v: Three, x: Pos, y: Pos) -> Position {
Position { horizontal: h, vertical: v, x: x, y: y }
}
pub fn middle() -> Position { p(Z, Z, relative(0.5), relative(0.5)) }
pub fn top_left() -> Position { p(N, P, absolute(0), absolute(0)) }
pub fn top_right() -> Position { p(P, P, absolute(0), absolute(0)) }
pub fn bottom_left() -> Position { p(N, N, absolute(0), absolute(0)) }
pub fn bottom_right() -> Position { p(P, N, absolute(0), absolute(0)) }
pub fn mid_left() -> Position { p(N, Z, absolute(0), relative(0.5)) }
pub fn mid_right() -> Position { p(P, Z, absolute(0), relative(0.5)) }
pub fn mid_top() -> Position { p(Z, P, relative(0.5), absolute(0)) }
pub fn mid_bottom() -> Position { p(Z, N, relative(0.5), absolute(0)) }
pub fn middle_at(x: Pos, y: Pos) -> Position { p(Z, Z, x, y) }
pub fn top_left_at(x: Pos, y: Pos) -> Position { p(N, P, x, y) }
pub fn top_right_at(x: Pos, y: Pos) -> Position { p(P, P, x, y) }
pub fn bottom_left_at(x: Pos, y: Pos) -> Position { p(N, N, x, y) }
pub fn bottom_right_at(x: Pos, y: Pos) -> Position { p(P, N, x, y) }
pub fn mid_left_at(x: Pos, y: Pos) -> Position { p(N, Z, x, y) }
pub fn mid_right_at(x: Pos, y: Pos) -> Position { p(P, Z, x, y) }
pub fn mid_top_at(x: Pos, y: Pos) -> Position { p(Z, P, x, y) }
pub fn mid_bottom_at(x: Pos, y: Pos) -> Position { p(Z, N, x, y) }
pub fn up() -> Direction { Direction::Up }
pub fn down() -> Direction { Direction::Down }
pub fn left() -> Direction { Direction::Left }
pub fn right() -> Direction { Direction::Right }
pub fn inward() -> Direction { Direction::In }
pub fn outward() -> Direction { Direction::Out }
///
/// CUSTOM NON-ELM FUNCTIONS
///
/// Used for rendering elmesque `Element`s.
pub struct Renderer<'a, C: 'a, G: 'a> {
context: Context,
backend: &'a mut G,
maybe_character_cache: Option<&'a mut C>,
}
impl<'a, C, G> Renderer<'a, C, G> {
/// Construct a renderer, used for rendering elmesque `Element`s.
pub fn new(context: Context, backend: &'a mut G) -> Renderer<'a, C, G> {
Renderer {
context: context,
backend: backend,
maybe_character_cache: None,
}
}
/// Builder method for constructing a Renderer with a GlyphCache for drawing text.
pub fn character_cache(self, character_cache: &'a mut C) -> Renderer<'a, C, G> {
Renderer { maybe_character_cache: Some(character_cache), ..self }
}
}
/// Draw an Element.
pub fn draw_element<'a, C: CharacterCache, G: Graphics<Texture=C::Texture>>(
element: &Element,
opacity: f32,
backend: &mut G,
maybe_character_cache: &mut Option<&mut C>,
context: Context,
) {
let Element { ref props, ref element } = *element;
// Crop the Element if some crop was given.
// We'll use the `DrawState::scissor` method for this.
//
// Because `DrawState`'s `scissor` `Rect` uses bottom-left origin coords, we'll have to convert
// from our centered-origin coordinate system.
//
// We'll also need to stretch our coords to match the correct viewport.draw_size.
let context = match props.crop {
Some((x, y, w, h)) => {
use utils::{clamp, map_range};
let Context { draw_state, .. } = context;
// Our view_dim is our virtual window size which is consistent no matter the display.
let view_dim = context.get_view_size();
// Our draw_dim is the actual window size in pixels. Our target crop area must be
// represented in this size.
let draw_dim = match context.viewport {
Some(viewport) => [viewport.draw_size[0] as f64, viewport.draw_size[1] as f64],
None => view_dim,
};
// Calculate the distance to the edges of the window from the center.
let left = -view_dim[0] / 2.0;
let right = view_dim[0] / 2.0;
let bottom = -view_dim[1] / 2.0;
let top = view_dim[1] / 2.0;
// We start with the x and y in the center of our crop area, however we need it to be
// at the top left of the crop area.
let left_x = x - w as f64 / 2.0;
let top_y = y - h as f64 / 2.0;
// Map the position at the top left of the crop area in view_dim to our draw_dim.
let x = map_range(left_x, left, right, 0, draw_dim[0] as i32);
let y = map_range(top_y, bottom, top, 0, draw_dim[1] as i32);
// Convert the w and h from our view_dim to the draw_dim.
let w_scale = draw_dim[0] / view_dim[0];
let h_scale = draw_dim[1] / view_dim[1];
let w = w * w_scale;
let h = h * h_scale;
// If we ended up with negative coords for the crop area, we'll use 0 instead as we
// can't represent the negative coords with `u16` (the target DrawState dimension type).
// We'll hold onto the lost negative values (x_neg and y_neg) so that we can compensate
// with the width and height.
let x_neg = if x < 0 { x } else { 0 };
let y_neg = if y < 0 { y } else { 0 };
let mut x = ::std::cmp::max(0, x) as u16;
let mut y = ::std::cmp::max(0, y) as u16;
let mut w = ::std::cmp::max(0, (w as i32 + x_neg)) as u16;
let mut h = ::std::cmp::max(0, (h as i32 + y_neg)) as u16;
// If there was already some scissor set, we must check for the intersection.
if let Some(rect) = draw_state.scissor {
if x + w < rect.x || rect.x + rect.w < x || y + h < rect.y || rect.y + rect.h < y {
// If there is no intersection, we have no scissor.
w = 0;
h = 0;
} else {
// If there is some intersection, calculate the overlapping rect.
let (a_l, a_r, a_b, a_t) = (x, x+w, y, y+h);
let (b_l, b_r, b_b, b_t) = (rect.x, rect.x+rect.w, rect.y, rect.y+rect.h);
let l = if a_l > b_l { a_l } else { b_l };
let r = if a_r < b_r { a_r } else { b_r };
let b = if a_b > b_b { a_b } else { b_b };
let t = if a_t < b_t { a_t } else { b_t };
x = l;
y = b;
w = r - l;
h = t - b;
}
}
Context { draw_state: draw_state.scissor(x, y, w, h), ..context }
},
None => context,
};
match *element {
Prim::Image(style, w, h, ref path) => {
let Properties { width, height, opacity, color, .. } = *props;
match style {
ImageStyle::Plain => {
// let image = graphics::Image {
// color: None,
// rectangle: None,
// source_rectangle: Some([src_x, src_y, w, h]),
// };
// let image = Image::new();
// let texture: &Texture = ::std::ops::Deref::deref(&texture);
// image.draw(texture, draw_state, matrix, backend);
unimplemented!();
},
ImageStyle::Fitted => {
unimplemented!();
},
ImageStyle::Cropped(x, y) => {
unimplemented!();
},
ImageStyle::Tiled => {
unimplemented!();
},
}
},
Prim::Container(position, ref element) => {
let Position { horizontal, vertical, x, y } = position;
let context = match (x, y) {
(Pos::Relative(x), Pos::Relative(y)) => context.trans(x as f64, y as f64),
(Pos::Absolute(x), Pos::Relative(y)) => Context {
transform: transform_2d::matrix(1.0, 0.0, 0.0, 1.0, x as f64, 0.0).0,
..context
}.trans(0.0, y as f64),
(Pos::Relative(x), Pos::Absolute(y)) => Context {
transform: transform_2d::matrix(1.0, 0.0, 0.0, 1.0, 0.0, y as f64).0,
..context
}.trans(x as f64, 0.0),
(Pos::Absolute(x), Pos::Absolute(y)) => Context {
transform: transform_2d::matrix(1.0, 0.0, 0.0, 1.0, x as f64, y as f64).0,
..context
},
};
let new_opacity = opacity * props.opacity;
draw_element(element, new_opacity, backend, maybe_character_cache, context);
}
Prim::Flow(direction, ref elements) => {
let mut context = context;
match direction {
Direction::Up | Direction::Down => {
let multi = if let Direction::Up = direction { 1.0 } else { -1.0 };
let mut half_prev_height = 0.0;
for element in elements.iter() {
let half_height = element.get_height() as f64 / 2.0;
let new_opacity = opacity * props.opacity;
draw_element(element, new_opacity, backend, maybe_character_cache, context);
let y_trans = half_height + half_prev_height;
context = context.trans(0.0, y_trans * multi);
half_prev_height = half_height;
}
},
Direction::Left | Direction::Right => {
let multi = if let Direction::Right = direction { 1.0 } else { -1.0 };
let mut half_prev_width = 0.0;
for element in elements.iter() {
let half_width = element.get_width() as f64 / 2.0;
let new_opacity = opacity * props.opacity;
draw_element(element, new_opacity, backend, maybe_character_cache, context);
let x_trans = half_width + half_prev_width;
context = context.trans(x_trans * multi, 0.0);
half_prev_width = half_width;
}
},
Direction::Out => {
for element in elements.iter() {
let new_opacity = opacity * props.opacity;
draw_element(element, new_opacity, backend, maybe_character_cache, context);
}
}
Direction::In => {
for element in elements.iter().rev() {
let new_opacity = opacity * props.opacity;
draw_element(element, new_opacity, backend, maybe_character_cache, context);
}
}
}
},
Prim::Collage(w, h, ref forms) => {
for form in forms.iter() {
let new_opacity = opacity * props.opacity;
form::draw_form(form, new_opacity, backend, maybe_character_cache, context);
}
},
Prim::Cleared(color, ref element) => {
backend.clear_color(color.to_fsa());
draw_element(element, opacity, backend, maybe_character_cache, context);
},
Prim::Spacer => {},
}
}
================================================
FILE: src/form.rs
================================================
//!
//! Ported from [elm-lang's `Graphics.Collage` module]
//! (https://github.com/elm-lang/core/blob/62b22218c42fb8ccc996c86bea450a14991ab815/src/Graphics/Collage.elm).
//!
//!
//! This module is for freeform graphics. You can shift, rotate, scale etc. All sorts of forms
//! including lines, shapes, images and elements.
//!
//! Collages use the same coordinate system you might see in an algebra or physics problem. The
//! origin (0, 0) is at the center of the collage, not the top left corner as in some other
//! graphics libraries. Furthermore, the y-axis points up, so shifting a form 10 units in the
//! y-axis will move it up screen.
//!
//! # Creating Forms
//! to_form, filled, textured, gradient, outlined, traced, text, outlined_text
//!
//! # Transforming Forms
//! shift, shift_x, shift_y, scale, rotate, alpha
//!
//! # Grouping Forms
//! Grouping forms makes it easier to write modular graphics code. You can create a form that is a
//! composite of many subforms. From there it is easy to transform it as a single unit.
//! group, group_transform
//!
//! # Shapes
//! rect, oval, square, circle, ngon, polygon
//!
//! # Paths
//! segment, path
//!
//! # Line Styles
//! solid, dashed, dotted, LineStyle, LineCap, LineJoin
//!
use color::{Color, Gradient};
use element::{self, Element, new_element};
use graphics::{self, Context, Graphics, Transformed};
use graphics::character::CharacterCache;
use std::f64::consts::PI;
use std::path::PathBuf;
use text::Text;
use transform_2d::{self, Transform2D};
/// A general, freeform 2D graphics structure.
#[derive(Clone, Debug)]
pub struct Form {
pub theta: f64,
pub scale: f64,
pub x: f64,
pub y: f64,
pub alpha: f32,
pub form: BasicForm,
}
#[derive(Clone, Debug)]
pub enum FillStyle {
Solid(Color),
Texture(PathBuf),
Grad(Gradient),
}
#[derive(Copy, Clone, Debug)]
pub enum LineCap {
Flat,
Round,
Padded,
}
#[derive(Copy, Clone, Debug)]
pub enum LineJoin {
Smooth,
Sharp(f64),
Clipped,
}
#[derive(Clone, Debug)]
pub struct LineStyle {
pub color: Color,
pub width: f64,
pub cap: LineCap,
pub join: LineJoin,
pub dashing: Vec<i64>,
pub dash_offset: i64,
}
impl LineStyle {
/// The default LineStyle.
pub fn default() -> LineStyle {
LineStyle {
color: ::color::black(),
width: 1.0,
cap: LineCap::Flat,
join: LineJoin::Sharp(10.0),
dashing: Vec::new(),
dash_offset: 0,
}
}
/// The LineStyle with some given width.
#[inline]
pub fn width(self, w: f64) -> LineStyle {
LineStyle { width: w, ..self }
}
}
/// Create a solid line style with a given color.
pub fn solid(color: Color) -> LineStyle {
LineStyle { color: color, ..LineStyle::default() }
}
/// Create a dashed line style with a given color. Dashing equals `[8, 4]`.
pub fn dashed(color: Color) -> LineStyle {
LineStyle { color: color, dashing: vec![8, 4], ..LineStyle::default() }
}
/// Create a dotted line style with a given color. Dashing equals `[3, 3]`.
pub fn dotted(color: Color) -> LineStyle {
LineStyle { color: color, dashing: vec![3, 3], ..LineStyle::default() }
}
/// The basic variants a Form can consist of.
#[derive(Clone, Debug)]
pub enum BasicForm {
PointPath(LineStyle, PointPath),
Shape(ShapeStyle, Shape),
OutlinedText(LineStyle, Text),
Text(Text),
Image(i32, i32, (i32, i32), PathBuf),
Element(Element),
Group(Transform2D, Vec<Form>),
}
/// Whether a shape is outlined or filled.
#[derive(Clone, Debug)]
pub enum ShapeStyle {
Line(LineStyle),
Fill(FillStyle),
}
impl Form {
fn new(basic_form: BasicForm) -> Form {
Form {
theta: 0.0,
scale: 1.0,
x: 0.0,
y: 0.0,
alpha: 1.0,
form: basic_form,
}
}
/// Move a form by the given amount. this is a relative translation so `shift(10.0, 10.0, form)
/// would move `form` ten pixels up and ten pixels to the right.
#[inline]
pub fn shift(self, x: f64, y: f64) -> Form {
Form { x: self.x + x, y: self.y + y, ..self }
}
/// Move a shape in the x direction. This is relative so `shift_x(10.0, form)` moves `form` 10
/// pixels to the right.
#[inline]
pub fn shift_x(self, x: f64) -> Form {
self.shift(x, 0.0)
}
/// Move a shape in the y direction. This is relative so `shift_y(10.0, form)` moves `form
/// upwards by 10 pixels.
#[inline]
pub fn shift_y(self, y: f64) -> Form {
self.shift(0.0, y)
}
/// Scale a form by a given factor. Scaling by 2 doubles both dimensions and quadruples the
/// area.
#[inline]
pub fn scale(self, scale: f64) -> Form {
Form { scale: self.scale * scale, ..self }
}
/// Rotate a form by a given angle. Rotate takes radians and turns things counterclockwise.
/// So to turn `form` 30 degrees to the left you would say `rotate(degrees(30), form)`.
#[inline]
pub fn rotate(self, theta: f64) -> Form {
Form { theta: self.theta + theta, ..self }
}
/// Set the alpha of a Form. The default is 1 and 0 is totally transparent.
#[inline]
pub fn alpha(self, alpha: f32) -> Form {
Form { alpha: alpha, ..self }
}
}
/// Turn any `Element` into a `Form`. This lets you use text, gifs, and video in your collage. This
/// means you can move, rotate, and scale an `Element` however you want.
pub fn to_form(element: Element) -> Form {
Form::new(BasicForm::Element(element))
}
/// Flatten many forms into a single `Form`. This lets you move and rotate them as a single unit,
/// making it possible to build small, modular components.
pub fn group(forms: Vec<Form>) -> Form {
Form::new(BasicForm::Group(transform_2d::identity(), forms))
}
/// Flatten many forms into a single `Form` and then apply a matrix transformation.
pub fn group_transform(matrix: Transform2D, forms: Vec<Form>) -> Form {
Form::new(BasicForm::Group(matrix, forms))
}
/// Trace a path with a given line style.
pub fn traced(style: LineStyle, path: PointPath) -> Form {
Form::new(BasicForm::PointPath(style, path))
}
/// Create a line with a given line style.
pub fn line(style: LineStyle, x1: f64, y1: f64, x2: f64, y2: f64) -> Form {
traced(style, segment((x1, y1), (x2, y2)))
}
/// Create a sprite from a sprite sheet. It cuts out a rectangle at a given position.
pub fn sprite(w: i32, h: i32, pos: (i32, i32), path: PathBuf) -> Form {
Form::new(BasicForm::Image(w, h, pos, path))
}
/// A collage is a collection of 2D forms. There are no strict positioning relationships between
/// forms, so you are free to do all kinds of 2D graphics.
pub fn collage(w: i32, h: i32, forms: Vec<Form>) -> Element {
new_element(w, h, element::Prim::Collage(w, h, forms))
}
/// A path described by a sequence of points.
#[derive(Clone, Debug)]
pub struct PointPath(pub Vec<(f64, f64)>);
/// Create a PointPath that follows a sequence of points.
pub fn point_path(points: Vec<(f64, f64)>) -> PointPath {
PointPath(points)
}
/// Create a PointPath along a given line segment.
pub fn segment(a: (f64, f64), b: (f64, f64)) -> PointPath {
PointPath(vec![a, b])
}
/// A shape described by its edges.
#[derive(Clone, Debug)]
pub struct Shape(pub Vec<(f64, f64)>);
impl Shape {
#[inline]
fn fill(self, style: FillStyle) -> Form {
Form::new(BasicForm::Shape(ShapeStyle::Fill(style), self))
}
/// Create a filled-in shape.
#[inline]
pub fn filled(self, color: Color) -> Form {
self.fill(FillStyle::Solid(color))
}
/// Create a textured shape.
/// The texture is described by some path and is tiled to fill the entire shape.
#[inline]
pub fn textured(self, path: PathBuf) -> Form {
self.fill(FillStyle::Texture(path))
}
/// Fill a shape with a gradient.
#[inline]
pub fn gradient(self, grad: Gradient) -> Form {
self.fill(FillStyle::Grad(grad))
}
/// Outline a shape with a given line style.
#[inline]
pub fn outlined(self, style: LineStyle) -> Form {
Form::new(BasicForm::Shape(ShapeStyle::Line(style), self))
}
}
/// Create an arbitrary polygon by specifying its corners in order. `polygon` will automatically
/// close all shapes, so the given list of points does not need to start and end with the same
/// position.
pub fn polygon(points: Vec<(f64, f64)>) -> Shape {
Shape(points)
}
/// A rectangle with a given width and height.
pub fn rect(w: f64, h: f64) -> Shape {
let hw = w / 2.0;
let hh = h / 2.0;
Shape(vec![ (0.0-hw, 0.0-hh), (0.0-hw, hh), (hw, hh), (hw, 0.0-hh) ])
}
/// A square with a given edge length.
pub fn square(n: f64) -> Shape {
rect(n, n)
}
/// An oval with a given width and height.
pub fn oval(w: f64, h: f64) -> Shape {
let n: usize = 50;
let t = 2.0 * PI / n as f64;
let hw = w / 2.0;
let hh = h / 2.0;
let f = |i: f64| (hw * (t*i).cos(), hh * (t*i).sin());
let points = (0..n-1).map(|i| f(i as f64)).collect();
Shape(points)
}
/// A circle with a given radius.
pub fn circle(r: f64) -> Shape {
let d = 2.0 * r;
oval(d, d)
}
/// A regular polygon with N sides. The first argument specifies the number of sides and the second
/// is the radius. So to create a pentagon with radius 30, you would say `ngon(5, 30.0)`
pub fn ngon(n: usize, r: f64) -> Shape {
let t = 2.0 * PI / n as f64;
let f = |i: f64| (r * (t*i).cos(), r * (t*i).sin());
let points = (0..n).map(|i| f(i as f64)).collect();
Shape(points)
}
/// Create some text. Details like size and color are part of the `Text` value itself, so you can
/// mix colors and sizes and fonts easily.
pub fn text(t: Text) -> Form {
Form::new(BasicForm::Text(t))
}
///
/// CUSTOM NON-ELM FUNCTIONS.
///
/// Normally Elm renders to html and javascript, however the aim of elmesque is to render to GL.
///
/// This function draws a form with some given transform using the generic [Piston graphics]
/// (https://github.com/PistonDevelopers/graphics) backend.
pub fn draw_form<'a, C: CharacterCache, G: Graphics<Texture=C::Texture>>(
form: &Form,
alpha: f32,
backend: &mut G,
maybe_character_cache: &mut Option<&mut C>,
context: Context,
) {
let Form { theta, scale, x, y, alpha, ref form } = *form;
let context = context.trans(x, y).scale(scale, scale).rot_rad(theta);
match *form {
BasicForm::PointPath(ref line_style, PointPath(ref points)) => {
// NOTE: join, dashing and dash_offset are not yet handled properly.
let LineStyle { color, width, cap, join, ref dashing, dash_offset } = *line_style;
let color = convert_color(color, alpha);
let mut draw_line = |(x1, y1), (x2, y2)| {
if dashing.is_empty() {
let line = match cap {
LineCap::Flat => graphics::Line::new(color, width / 2.0),
LineCap::Round => graphics::Line::new_round(color, width / 2.0),
LineCap::Padded => unimplemented!(),
};
line.draw([x1, y1, x2, y2], &context.draw_state, context.transform, backend);
} else {
unimplemented!();
}
};
for window in points.windows(2) {
let (a, b) = (window[0], window[1]);
draw_line(a, b);
}
},
BasicForm::Shape(ref shape_style, Shape(ref points)) => {
match *shape_style {
ShapeStyle::Line(ref line_style) => {
// NOTE: join, dashing and dash_offset are not yet handled properly.
let LineStyle { color, width, cap, join, ref dashing, dash_offset } = *line_style;
let color = convert_color(color, alpha);
let mut draw_line = |(x1, y1), (x2, y2)| {
let line = match cap {
LineCap::Flat => graphics::Line::new(color, width / 2.0),
LineCap::Round => graphics::Line::new_round(color, width / 2.0),
LineCap::Padded => unimplemented!(),
};
line.draw([x1, y1, x2, y2], &context.draw_state, context.transform, backend);
};
for window in points.windows(2) {
let (a, b) = (window[0], window[1]);
draw_line(a, b);
}
if points.len() > 2 {
draw_line(points[points.len()-1], points[0])
}
},
ShapeStyle::Fill(ref fill_style) => match *fill_style {
FillStyle::Solid(color) => {
let color = convert_color(color, alpha);
let polygon = graphics::Polygon::new(color);
let points: Vec<_> = points.iter().map(|&(x, y)| [x, y]).collect();
polygon.draw(&points[..], &context.draw_state, context.transform, backend);
},
FillStyle::Texture(ref path) => {
unimplemented!();
},
FillStyle::Grad(ref gradient) => {
unimplemented!();
},
},
}
},
BasicForm::OutlinedText(ref line_style, ref text) => {
unimplemented!();
},
BasicForm::Text(ref text) => {
let context = context.scale(1.0, -1.0);
if let Some(ref mut character_cache) = *maybe_character_cache {
use text::Style as TextStyle;
use text::Position as TextPosition;
use text::TextUnit;
let (total_width, max_height) = text.sequence.iter().fold((0.0, 0.0), |(w, h), unit| {
let TextUnit { ref string, ref style } = *unit;
let TextStyle { ref typeface, height, color, bold, italic, line, monospace } = *style;
let height = height.unwrap_or(16.0);
let new_total_width = w + character_cache.width(height as u32, &string);
let new_max_height = if height > h { height } else { h };
(new_total_width, new_max_height)
});
let x_offset = match text.position {
TextPosition::Center => -(total_width / 2.0).floor(),
TextPosition::ToLeft => -total_width.floor(),
TextPosition::ToRight => 0.0
};
let y_offset = (max_height / 3.0).floor(); // TODO: FIX THIS (3.0)
let context = context.trans(x_offset, y_offset);
for unit in text.sequence.iter() {
let TextUnit { ref string, ref style } = *unit;
let TextStyle { ref typeface, height, color, bold, italic, line, monospace } = *style;
let height = height.unwrap_or(16.0).floor();
let color = convert_color(color, alpha);
graphics::text::Text::new_color(color, height as u32)
.round()
.draw(&string[..], *character_cache, &context.draw_state, context.transform, backend);
}
}
},
BasicForm::Image(src_x, src_y, (w, h), ref path) => {
// let image = graphics::Image {
// color: None,
// rectangle: None,
// source_rectangle: Some([src_x, src_y, w, h]),
// };
// let texture: &Texture = ::std::ops::Deref::deref(&texture);
// image.draw(texture, draw_state, matrix, backend);
unimplemented!();
},
BasicForm::Group(ref group_transform, ref forms) => {
let Transform2D(matrix) = Transform2D(context.transform.clone())
.multiply(group_transform.clone());
let context = Context { transform: matrix, ..context };
for form in forms.iter() {
draw_form(form, alpha, backend, maybe_character_cache, context);
}
},
BasicForm::Element(ref element) =>
element::draw_element(element, alpha, backend, maybe_character_cache, context),
}
}
/// Convert an elmesque color to a piston-graphics color.
fn convert_color(color: Color, alpha: f32) -> [f32; 4] {
use color::hsl_to_rgb;
let ((r, g, b), a) = match color {
Color::Hsla(h, s, l, a) => (hsl_to_rgb(h, s, l), a),
Color::Rgba(r, g, b, a) => ((r, g, b), a),
};
[r, g, b, a * alpha]
}
================================================
FILE: src/lib.rs
================================================
//!
//! This crate is an attempt at porting Elm's incredibly useful std graphics modules.
//!
//! Visit [elm-lang.org](http://elm-lang.org/).
//!
//!
//! All credit goes to Evan Czaplicki for all algorithms included within.
//!
//! Ported to Rust by Mitchell Nordine.
//!
extern crate graphics;
extern crate num;
extern crate rand;
extern crate rustc_serialize;
extern crate vecmath;
pub use color as colour;
pub use element::{Element, Renderer};
pub use form::{Form};
pub mod color;
pub mod element;
pub mod form;
pub mod text;
pub mod transform_2d;
pub mod utils;
================================================
FILE: src/text.rs
================================================
use color::{black, Color};
use std::path::PathBuf;
/// Drawable Text.
#[derive(Clone, Debug)]
pub struct Text {
pub sequence: Vec<TextUnit>,
pub position: Position,
}
#[derive(Clone, Debug)]
pub struct TextUnit {
pub string: String,
pub style: Style,
}
/// Styles for lines on text. This allows you to add an underline, an overline, or strike out text.
#[derive(Copy, Clone, Debug)]
pub enum Line {
Under,
Over,
Through,
}
/// Text position relative to center point
#[derive(Copy, Clone, Debug)]
pub enum Position {
Center,
ToLeft,
ToRight
}
/// Represents all the ways you can style `Text`. If the `type_face` list is empty or the `height`
/// is `None`, the users will fall back on their default settings. The following `Style` is black,
/// 16 pixel tall, underlined, and Times New Roman (assuming that typeface is available on the
/// user's computer):
///
/// Style {
/// type_face: Some("Times New Roman"),
/// height: Some(16),
/// color: black(),
/// bold: false,
/// italic: false,
/// line: Some(Line::Under),
/// }
///
#[derive(Clone, Debug)]
pub struct Style {
pub typeface: Option<PathBuf>,
pub height: Option<f64>,
pub color: Color,
pub bold: bool,
pub italic: bool,
pub line: Option<Line>,
pub monospace: bool,
}
impl Style {
pub fn default() -> Style {
Style {
typeface: None,
height: None,
color: black(),
bold: false,
italic: false,
line: None,
monospace: false,
}
}
}
impl Text {
/// Convert a string into text which can be styled and displayed.
pub fn from_string(string: String) -> Text {
Text {
sequence: vec![TextUnit { string: string, style: Style::default(), }],
position: Position::Center
}
}
/// Text with nothing in it.
pub fn empty() -> Text {
Text::from_string("".to_string())
}
/// Put two chunks of text together.
#[inline]
pub fn append(mut self, other: Text) -> Text {
self.sequence.extend(other.sequence.into_iter());
self
}
/// Put many chunks of text together.
pub fn concat(texts: Vec<Text>) -> Text {
let position = texts.get(0).map(|t| t.position).unwrap_or(Position::Center);
Text {
sequence: texts.into_iter()
.flat_map(|Text { sequence, position }| sequence.into_iter())
.collect(),
position: position
}
}
/// Put many chunks of text together with a separator.
pub fn join(separator: Text, texts: Vec<Text>) -> Text {
texts.into_iter().fold(Text::empty(), |texts, text| {
texts.append(text).append(separator.clone())
})
}
/// Set the style of some text. For example, if you design a `Style` called `foorter_style` that is
/// specifically for the bottom of your page, you could apply it to text like this:
///
/// style(footer_style, from_string("the old prince / 2007"))
///
#[inline]
pub fn style(self, style: Style) -> Text {
let string = String::from_utf8(self.sequence.into_iter().flat_map(|unit| {
unit.string.into_bytes().into_iter()
}).collect()).unwrap();
Text {
sequence: vec![TextUnit { string: string, style: style }],
..self
}
}
/// Provide a path of a typeface to be used for some text.
#[inline]
pub fn typeface(mut self, path: PathBuf) -> Text {
for unit in self.sequence.iter_mut() {
unit.style.typeface = Some(path.clone());
}
self
}
/// Switch to a monospace typeface. Good for code snippets.
///
/// monospace(from_string("(0..3).fold(0, |a, b| a + b)"))
///
#[inline]
pub fn monospace(mut self) -> Text {
for unit in self.sequence.iter_mut() {
unit.style.monospace = true;
}
self
}
/// Set the height of some text in pixels.
#[inline]
pub fn height(mut self, h: f64) -> Text {
for unit in self.sequence.iter_mut() {
unit.style.height = Some(h);
}
self
}
/// Set the color of some text.
#[inline]
pub fn color(mut self, color: Color) -> Text {
for unit in self.sequence.iter_mut() {
unit.style.color = color;
}
self
}
/// Make the text bold.
#[inline]
pub fn bold(mut self) -> Text {
for unit in self.sequence.iter_mut() {
unit.style.bold = true;
}
self
}
/// Make the text italic.
#[inline]
pub fn italic(mut self) -> Text {
for unit in self.sequence.iter_mut() {
unit.style.italic = true;
}
self
}
/// Put lines on text.
#[inline]
pub fn line(mut self, line: Line) -> Text {
for unit in self.sequence.iter_mut() {
unit.style.line = Some(line);
}
self
}
/// Change the text position relative to it's center point
#[inline]
pub fn position(mut self, position: Position) -> Text {
self.position = position;
self
}
}
================================================
FILE: src/transform_2d.rs
================================================
//!
//! Ported from [elm-lang's Transform2D module]
//! (https://github.com/elm-lang/core/blob/62b22218c42fb8ccc996c86bea450a14991ab815/src/Transform2D.elm)
//!
//!
//! A library for performing 2D matrix transformations. It is used primarily with the
//! `group_transform` function from the `form` module and allows you to do things like rotation,
//! scaling, translation, shearing and reflection.
//!
//! Note that all the matrices in this library are 3*3 matrices of homogeneous coordinates, used
//! for affine transformations. Since the bottom row is always `0 0 1` in these matrices, it is
//! omitted in the diagrams below.
//!
use vecmath::{mat2x3_id, Matrix2x3, row_mat2x3_mul};
pub type Matrix2d = Matrix2x3<f64>;
/// Represents a 2D transform.
#[derive(Clone, Debug)]
pub struct Transform2D(pub Matrix2d);
impl Transform2D {
/// Multiply two transforms together.
///
/// ma mb mx na nb nx
/// mc md my . nc nd ny
/// 0 0 1 0 0 1
///
#[inline]
pub fn multiply(self, other: Transform2D) -> Transform2D {
let (Transform2D(m), Transform2D(n)) = (self, other);
Transform2D(row_mat2x3_mul(m, n))
}
}
/// Create an identity transform. Transforming by the identity does not change anything, but it can
/// come in handy as a default or base case.
///
/// 1 0 0
/// 0 1 0
///
#[inline]
pub fn identity() -> Transform2D {
Transform2D(mat2x3_id())
}
/// Creates a transformation matrix. This lets you create transforms such as scales, shears,
/// reflections and translations.
///
/// a b x
/// c d y
///
#[inline]
pub fn matrix(a: f64, b: f64, c: f64, d: f64, x: f64, y: f64) -> Transform2D {
Transform2D([ [a, b, x], [c, d, y] ])
}
/// Create a [rotation matrix](http://en.wikipedia.org/wiki/Rotation_matrix). Given an angle t, it
/// creates a counterclockwise rotation matrix.
///
/// cos t -sin t 0
/// sin t cos t 0
///
#[inline]
pub fn rotation(t: f64) -> Transform2D {
Transform2D([ [t.cos(), -t.sin(), 0.0], [t.sin(), t.cos(), 0.0] ])
}
/// Creates a transformation matrix for translation.
///
/// 1 0 x
/// 0 1 y
///
#[inline]
pub fn translation(x: f64, y: f64) -> Transform2D {
matrix(1.0, 0.0, 0.0, 1.0, x, y)
}
/// Creates a transformation matrix for scaling by all directions.
///
/// s 0 0
/// 0 s 0
///
#[inline]
pub fn scale(s: f64) -> Transform2D {
matrix(s, 0.0, 0.0, s, 0.0, 0.0)
}
/// Creates a transformation for horizontal scaling.
#[inline]
pub fn scale_x(s: f64) -> Transform2D {
matrix(s, 0.0, 0.0, 1.0, 0.0, 0.0)
}
/// Creates a transformation for vertical scaling.
#[inline]
pub fn scale_y(s: f64) -> Transform2D {
matrix(1.0, 0.0, 0.0, s, 0.0, 0.0)
}
================================================
FILE: src/utils.rs
================================================
use num::{Float, NumCast};
use num::PrimInt as Int;
use num::traits::cast;
use std::f32::consts::PI;
/// Clamp a f32 between 0f32 and 1f32.
pub fn clampf32(f: f32) -> f32 {
if f < 0f32 { 0f32 } else if f > 1f32 { 1f32 } else { f }
}
/// Convert degrees to radians.
pub fn degrees<F: Float + NumCast>(d: F) -> F {
d * cast(PI / 180.0).unwrap()
}
/// Convert turns to radians.
pub fn turns<F: Float + NumCast>(t: F) -> F {
let f: F = cast(2.0 * PI).unwrap();
f * t
}
/// The modulo function.
#[inline]
pub fn modulo<I: Int>(a: I, b: I) -> I {
match a % b {
r if (r > I::zero() && b < I::zero())
|| (r < I::zero() && b > I::zero()) => r + b,
r => r,
}
}
/// Modulo float.
pub fn fmod(f: f32, n: i32) -> f32 {
let i = f.floor() as i32;
modulo(i, n) as f32 + f - i as f32
}
/// Return the min between to floats.
pub fn min(a: f32, b: f32) -> f32 {
if a <= b { a } else { b }
}
/// Return the max between to floats.
pub fn max(a: f32, b: f32) -> f32 {
if a >= b { a } else { b }
}
/// Clamp a value to a range.
#[inline]
pub fn clamp<T: PartialOrd>(val: T, min: T, max: T) -> T {
if val < min { min } else { if val > max { max } else { val } }
}
/// Map a value from a given range to a new given range.
pub fn map_range<X: NumCast, Y: NumCast>
(val: X, in_min: X, in_max: X, out_min: Y, out_max: Y) -> Y {
let val_f: f64 = NumCast::from(val).unwrap();
let in_min_f: f64 = NumCast::from(in_min).unwrap();
let in_max_f: f64 = NumCast::from(in_max).unwrap();
let out_min_f: f64 = NumCast::from(out_min).unwrap();
let out_max_f: f64 = NumCast::from(out_max).unwrap();
NumCast::from(
(val_f - in_min_f) / (in_max_f - in_min_f) * (out_max_f - out_min_f) + out_min_f
).unwrap()
}
gitextract_r6tt53c7/
├── .gitignore
├── .travis.yml
├── Cargo.toml
├── ELM_LICENSE.md
├── README.md
├── assets/
│ └── NotoSans/
│ └── LICENSE-2.0.txt
├── examples/
│ └── graphics.rs
└── src/
├── color.rs
├── element.rs
├── form.rs
├── lib.rs
├── text.rs
├── transform_2d.rs
└── utils.rs
SYMBOL INDEX (227 symbols across 7 files)
FILE: examples/graphics.rs
function main (line 13) | fn main() {
function elmesque_demo_form (line 60) | pub fn elmesque_demo_form(secs: f64) -> Form {
FILE: src/color.rs
type Color (line 22) | pub enum Color {
method complement (line 108) | pub fn complement(self) -> Color {
method luminance (line 119) | pub fn luminance(&self) -> f32 {
method plain_contrast (line 128) | pub fn plain_contrast(self) -> Color {
method to_hsl (line 133) | pub fn to_hsl(self) -> Hsla {
method to_rgb (line 144) | pub fn to_rgb(self) -> Rgba {
method to_fsa (line 155) | pub fn to_fsa(self) -> [f32; 4] {
method to_byte_fsa (line 161) | pub fn to_byte_fsa(self) -> [u8; 4] {
method to_hex (line 168) | pub fn to_hex(self) -> String {
method with_luminance (line 175) | pub fn with_luminance(self, l: f32) -> Color {
method alpha (line 181) | pub fn alpha(self, alpha: f32) -> Color {
method with_alpha (line 189) | pub fn with_alpha(self, a: f32) -> Color {
method highlighted (line 197) | pub fn highlighted(self) -> Color {
method clicked (line 214) | pub fn clicked(&self) -> Color {
method invert (line 231) | pub fn invert(self) -> Color {
method red (line 237) | pub fn red(&self) -> f32 {
method green (line 243) | pub fn green(&self) -> f32 {
method blue (line 249) | pub fn blue(&self) -> f32 {
method set_red (line 255) | pub fn set_red(&mut self, r: f32) {
method set_green (line 261) | pub fn set_green(&mut self, g: f32) {
method set_blue (line 267) | pub fn set_blue(&mut self, b: f32) {
type Colour (line 30) | pub type Colour = Color;
function rgba (line 36) | pub fn rgba(r: f32, g: f32, b: f32, a: f32) -> Color {
function rgb (line 43) | pub fn rgb(r: f32, g: f32, b: f32) -> Color {
function rgba_bytes (line 51) | pub fn rgba_bytes(r: u8, g: u8, b: u8, a: f32) -> Color {
function rgb_bytes (line 58) | pub fn rgb_bytes(r: u8, g: u8, b: u8) -> Color {
function hsla (line 66) | pub fn hsla(hue: f32, saturation: f32, lightness: f32, alpha: f32) -> Co...
function hsl (line 83) | pub fn hsl(hue: f32, saturation: f32, lightness: f32) -> Color {
function grayscale (line 89) | pub fn grayscale(p: f32) -> Color {
function greyscale (line 93) | pub fn greyscale(p: f32) -> Color {
function random (line 99) | pub fn random() -> Color {
type Hsla (line 277) | pub struct Hsla(pub f32, pub f32, pub f32, pub f32);
type Rgba (line 282) | pub struct Rgba(pub f32, pub f32, pub f32, pub f32);
function f32_to_byte (line 287) | pub fn f32_to_byte(c: f32) -> u8 { (c * 255.0) as u8 }
function rgb_to_hsl (line 291) | pub fn rgb_to_hsl(r: f32, g: f32, b: f32) -> (f32, f32, f32) {
function hsl_to_rgb (line 313) | pub fn hsl_to_rgb(hue: f32, saturation: f32, lightness: f32) -> (f32, f3...
type Gradient (line 334) | pub enum Gradient {
function linear (line 346) | pub fn linear(start: (f64, f64), end: (f64, f64), colors: Vec<(f64, Colo...
function radial (line 352) | pub fn radial(start: (f64, f64), start_r: f64,
function light_red (line 367) | pub fn light_red() -> Color { rgb_bytes(239 , 41 , 41 ) }
function red (line 369) | pub fn red() -> Color { rgb_bytes(204 , 0 , 0 ) }
function dark_red (line 371) | pub fn dark_red() -> Color { rgb_bytes(164 , 0 , 0 ) }
function light_orange (line 374) | pub fn light_orange() -> Color { rgb_bytes(252 , 175 , 62 ) }
function orange (line 376) | pub fn orange() -> Color { rgb_bytes(245 , 121 , 0 ) }
function dark_orange (line 378) | pub fn dark_orange() -> Color { rgb_bytes(206 , 92 , 0 ) }
function light_yellow (line 381) | pub fn light_yellow() -> Color { rgb_bytes(255 , 233 , 79 ) }
function yellow (line 383) | pub fn yellow() -> Color { rgb_bytes(237 , 212 , 0 ) }
function dark_yellow (line 385) | pub fn dark_yellow() -> Color { rgb_bytes(196 , 160 , 0 ) }
function light_green (line 388) | pub fn light_green() -> Color { rgb_bytes(138 , 226 , 52 ) }
function green (line 390) | pub fn green() -> Color { rgb_bytes(115 , 210 , 22 ) }
function dark_green (line 392) | pub fn dark_green() -> Color { rgb_bytes(78 , 154 , 6 ) }
function light_blue (line 395) | pub fn light_blue() -> Color { rgb_bytes(114 , 159 , 207) }
function blue (line 397) | pub fn blue() -> Color { rgb_bytes(52 , 101 , 164) }
function dark_blue (line 399) | pub fn dark_blue() -> Color { rgb_bytes(32 , 74 , 135) }
function light_purple (line 402) | pub fn light_purple() -> Color { rgb_bytes(173 , 127 , 168) }
function purple (line 404) | pub fn purple() -> Color { rgb_bytes(117 , 80 , 123) }
function dark_purple (line 406) | pub fn dark_purple() -> Color { rgb_bytes(92 , 53 , 102) }
function light_brown (line 409) | pub fn light_brown() -> Color { rgb_bytes(233 , 185 , 110) }
function brown (line 411) | pub fn brown() -> Color { rgb_bytes(193 , 125 , 17 ) }
function dark_brown (line 413) | pub fn dark_brown() -> Color { rgb_bytes(143 , 89 , 2 ) }
function black (line 416) | pub fn black() -> Color { rgb_bytes(0 , 0 , 0 ) }
function white (line 418) | pub fn white() -> Color { rgb_bytes(255 , 255 , 255) }
function light_gray (line 421) | pub fn light_gray() -> Color { rgb_bytes(238 , 238 , 236) }
function gray (line 423) | pub fn gray() -> Color { rgb_bytes(211 , 215 , 207) }
function dark_gray (line 425) | pub fn dark_gray() -> Color { rgb_bytes(186 , 189 , 182) }
function light_grey (line 428) | pub fn light_grey() -> Color { rgb_bytes(238 , 238 , 236) }
function grey (line 430) | pub fn grey() -> Color { rgb_bytes(211 , 215 , 207) }
function dark_grey (line 432) | pub fn dark_grey() -> Color { rgb_bytes(186 , 189 , 182) }
function light_charcoal (line 435) | pub fn light_charcoal() -> Color { rgb_bytes(136 , 138 , 133) }
function charcoal (line 437) | pub fn charcoal() -> Color { rgb_bytes(85 , 87 , 83 ) }
function dark_charcoal (line 439) | pub fn dark_charcoal() -> Color { rgb_bytes(46 , 52 , 54 ) }
type Colorable (line 444) | pub trait Colorable: Sized {
method color (line 447) | fn color(self, color: Color) -> Self;
method rgba (line 450) | fn rgba(self, r: f32, g: f32, b: f32, a: f32) -> Self {
method rgb (line 455) | fn rgb(self, r: f32, g: f32, b: f32) -> Self {
method hsla (line 460) | fn hsla(self, h: f32, s: f32, l: f32, a: f32) -> Self {
method hsl (line 465) | fn hsl(self, h: f32, s: f32, l: f32) -> Self {
FILE: src/element.rs
type Properties (line 66) | pub struct Properties {
type Element (line 80) | pub struct Element {
method width (line 90) | pub fn width(self, new_width: i32) -> Element {
method height (line 106) | pub fn height(self, new_height: i32) -> Element {
method size (line 122) | pub fn size(self, new_w: i32, new_h: i32) -> Element {
method opacity (line 128) | pub fn opacity(mut self, opacity: f32) -> Element {
method color (line 135) | pub fn color(mut self, color: Color) -> Element {
method crop (line 142) | pub fn crop(self, x: f64, y: f64, w: f64, h: f64) -> Element {
method container (line 151) | pub fn container(self, w: i32, h: i32, pos: Position) -> Element {
method clear (line 158) | pub fn clear(self, color: Color) -> Element {
method above (line 165) | pub fn above(self, other: Element) -> Element {
method below (line 173) | pub fn below(self, other: Element) -> Element {
method beside (line 180) | pub fn beside(self, other: Element) -> Element {
method get_width (line 187) | pub fn get_width(&self) -> i32 { self.props.width }
method get_height (line 190) | pub fn get_height(&self) -> i32 { self.props.height }
method get_size (line 193) | pub fn get_size(&self) -> (i32, i32) { (self.props.width, self.props.h...
method draw (line 197) | pub fn draw<'a, C, G>(&self, renderer: &mut Renderer<'a, C, G>)
method is_over (line 213) | pub fn is_over(&self, x: i32, y: i32) -> bool {
function size_of (line 220) | pub fn size_of(e: &Element) -> (i32, i32) {
function new_element (line 228) | pub fn new_element(w: i32, h: i32, element: Prim) -> Element {
function spacer (line 243) | pub fn spacer(w: i32, h: i32) -> Element {
function empty (line 249) | pub fn empty() -> Element {
type Prim (line 256) | pub enum Prim {
type ImageStyle (line 268) | pub enum ImageStyle {
function image (line 277) | pub fn image(w: i32, h: i32, path: PathBuf) -> Element {
function fitted_image (line 283) | pub fn fitted_image(w: i32, h: i32, path: PathBuf) -> Element {
function cropped_image (line 289) | pub fn cropped_image(x: i32, y: i32, w: i32, h: i32, path: PathBuf) -> E...
function tiled_image (line 294) | pub fn tiled_image(w: i32, h: i32, path: PathBuf) -> Element {
type Three (line 300) | pub enum Three { P, Z, N }
type Pos (line 302) | pub enum Pos { Absolute(i32), Relative(f32) }
type Position (line 306) | pub struct Position {
type Direction (line 315) | pub enum Direction { Up, Down, Left, Right, In, Out }
function flow (line 320) | pub fn flow(dir: Direction, elements: Vec<Element>) -> Element {
function layers (line 335) | pub fn layers(elements: Vec<Element>) -> Element {
function absolute (line 343) | pub fn absolute(i: i32) -> Pos { Pos::Absolute(i) }
function relative (line 344) | pub fn relative(f: f32) -> Pos { Pos::Relative(f) }
function p (line 347) | fn p(h: Three, v: Three, x: Pos, y: Pos) -> Position {
function middle (line 351) | pub fn middle() -> Position { p(Z, Z, relative(0.5), relative(0.5)) }
function top_left (line 352) | pub fn top_left() -> Position { p(N, P, absolute(0), absolute(0)) }
function top_right (line 353) | pub fn top_right() -> Position { p(P, P, absolute(0), absolute(0)) }
function bottom_left (line 354) | pub fn bottom_left() -> Position { p(N, N, absolute(0), absolute(0)) }
function bottom_right (line 355) | pub fn bottom_right() -> Position { p(P, N, absolute(0), absolute(0)) }
function mid_left (line 356) | pub fn mid_left() -> Position { p(N, Z, absolute(0), relative(0.5)) }
function mid_right (line 357) | pub fn mid_right() -> Position { p(P, Z, absolute(0), relative(0.5)) }
function mid_top (line 358) | pub fn mid_top() -> Position { p(Z, P, relative(0.5), absolute(0)) }
function mid_bottom (line 359) | pub fn mid_bottom() -> Position { p(Z, N, relative(0.5), absolute(0)) }
function middle_at (line 361) | pub fn middle_at(x: Pos, y: Pos) -> Position { p(Z, Z, x, y) }
function top_left_at (line 362) | pub fn top_left_at(x: Pos, y: Pos) -> Position { p(N, P, x, y) }
function top_right_at (line 363) | pub fn top_right_at(x: Pos, y: Pos) -> Position { p(P, P, x, y) }
function bottom_left_at (line 364) | pub fn bottom_left_at(x: Pos, y: Pos) -> Position { p(N, N, x, y) }
function bottom_right_at (line 365) | pub fn bottom_right_at(x: Pos, y: Pos) -> Position { p(P, N, x, y) }
function mid_left_at (line 366) | pub fn mid_left_at(x: Pos, y: Pos) -> Position { p(N, Z, x, y) }
function mid_right_at (line 367) | pub fn mid_right_at(x: Pos, y: Pos) -> Position { p(P, Z, x, y) }
function mid_top_at (line 368) | pub fn mid_top_at(x: Pos, y: Pos) -> Position { p(Z, P, x, y) }
function mid_bottom_at (line 369) | pub fn mid_bottom_at(x: Pos, y: Pos) -> Position { p(Z, N, x, y) }
function up (line 371) | pub fn up() -> Direction { Direction::Up }
function down (line 372) | pub fn down() -> Direction { Direction::Down }
function left (line 373) | pub fn left() -> Direction { Direction::Left }
function right (line 374) | pub fn right() -> Direction { Direction::Right }
function inward (line 375) | pub fn inward() -> Direction { Direction::In }
function outward (line 376) | pub fn outward() -> Direction { Direction::Out }
type Renderer (line 392) | pub struct Renderer<'a, C: 'a, G: 'a> {
function new (line 401) | pub fn new(context: Context, backend: &'a mut G) -> Renderer<'a, C, G> {
function character_cache (line 410) | pub fn character_cache(self, character_cache: &'a mut C) -> Renderer<'a,...
function draw_element (line 419) | pub fn draw_element<'a, C: CharacterCache, G: Graphics<Texture=C::Textur...
FILE: src/form.rs
type Form (line 48) | pub struct Form {
method new (line 155) | fn new(basic_form: BasicForm) -> Form {
method shift (line 170) | pub fn shift(self, x: f64, y: f64) -> Form {
method shift_x (line 178) | pub fn shift_x(self, x: f64) -> Form {
method shift_y (line 186) | pub fn shift_y(self, y: f64) -> Form {
method scale (line 194) | pub fn scale(self, scale: f64) -> Form {
method rotate (line 202) | pub fn rotate(self, theta: f64) -> Form {
method alpha (line 209) | pub fn alpha(self, alpha: f32) -> Form {
type FillStyle (line 59) | pub enum FillStyle {
type LineCap (line 67) | pub enum LineCap {
type LineJoin (line 75) | pub enum LineJoin {
type LineStyle (line 83) | pub struct LineStyle {
method default (line 96) | pub fn default() -> LineStyle {
method width (line 109) | pub fn width(self, w: f64) -> LineStyle {
function solid (line 117) | pub fn solid(color: Color) -> LineStyle {
function dashed (line 122) | pub fn dashed(color: Color) -> LineStyle {
function dotted (line 127) | pub fn dotted(color: Color) -> LineStyle {
type BasicForm (line 134) | pub enum BasicForm {
type ShapeStyle (line 147) | pub enum ShapeStyle {
function to_form (line 218) | pub fn to_form(element: Element) -> Form {
function group (line 225) | pub fn group(forms: Vec<Form>) -> Form {
function group_transform (line 231) | pub fn group_transform(matrix: Transform2D, forms: Vec<Form>) -> Form {
function traced (line 237) | pub fn traced(style: LineStyle, path: PointPath) -> Form {
function line (line 243) | pub fn line(style: LineStyle, x1: f64, y1: f64, x2: f64, y2: f64) -> Form {
function sprite (line 249) | pub fn sprite(w: i32, h: i32, pos: (i32, i32), path: PathBuf) -> Form {
function collage (line 256) | pub fn collage(w: i32, h: i32, forms: Vec<Form>) -> Element {
type PointPath (line 263) | pub struct PointPath(pub Vec<(f64, f64)>);
function point_path (line 267) | pub fn point_path(points: Vec<(f64, f64)>) -> PointPath {
function segment (line 273) | pub fn segment(a: (f64, f64), b: (f64, f64)) -> PointPath {
type Shape (line 280) | pub struct Shape(pub Vec<(f64, f64)>);
method fill (line 286) | fn fill(self, style: FillStyle) -> Form {
method filled (line 293) | pub fn filled(self, color: Color) -> Form {
method textured (line 301) | pub fn textured(self, path: PathBuf) -> Form {
method gradient (line 308) | pub fn gradient(self, grad: Gradient) -> Form {
method outlined (line 315) | pub fn outlined(self, style: LineStyle) -> Form {
function polygon (line 325) | pub fn polygon(points: Vec<(f64, f64)>) -> Shape {
function rect (line 331) | pub fn rect(w: f64, h: f64) -> Shape {
function square (line 339) | pub fn square(n: f64) -> Shape {
function oval (line 345) | pub fn oval(w: f64, h: f64) -> Shape {
function circle (line 357) | pub fn circle(r: f64) -> Shape {
function ngon (line 365) | pub fn ngon(n: usize, r: f64) -> Shape {
function text (line 375) | pub fn text(t: Text) -> Form {
function draw_form (line 396) | pub fn draw_form<'a, C: CharacterCache, G: Graphics<Texture=C::Texture>>(
function convert_color (line 531) | fn convert_color(color: Color, alpha: f32) -> [f32; 4] {
FILE: src/text.rs
type Text (line 8) | pub struct Text {
method from_string (line 80) | pub fn from_string(string: String) -> Text {
method empty (line 88) | pub fn empty() -> Text {
method append (line 94) | pub fn append(mut self, other: Text) -> Text {
method concat (line 100) | pub fn concat(texts: Vec<Text>) -> Text {
method join (line 111) | pub fn join(separator: Text, texts: Vec<Text>) -> Text {
method style (line 123) | pub fn style(self, style: Style) -> Text {
method typeface (line 135) | pub fn typeface(mut self, path: PathBuf) -> Text {
method monospace (line 147) | pub fn monospace(mut self) -> Text {
method height (line 156) | pub fn height(mut self, h: f64) -> Text {
method color (line 165) | pub fn color(mut self, color: Color) -> Text {
method bold (line 174) | pub fn bold(mut self) -> Text {
method italic (line 183) | pub fn italic(mut self) -> Text {
method line (line 192) | pub fn line(mut self, line: Line) -> Text {
method position (line 201) | pub fn position(mut self, position: Position) -> Text {
type TextUnit (line 15) | pub struct TextUnit {
type Line (line 22) | pub enum Line {
type Position (line 30) | pub enum Position {
type Style (line 52) | pub struct Style {
method default (line 63) | pub fn default() -> Style {
FILE: src/transform_2d.rs
type Matrix2d (line 18) | pub type Matrix2d = Matrix2x3<f64>;
type Transform2D (line 22) | pub struct Transform2D(pub Matrix2d);
method multiply (line 33) | pub fn multiply(self, other: Transform2D) -> Transform2D {
function identity (line 47) | pub fn identity() -> Transform2D {
function matrix (line 58) | pub fn matrix(a: f64, b: f64, c: f64, d: f64, x: f64, y: f64) -> Transfo...
function rotation (line 69) | pub fn rotation(t: f64) -> Transform2D {
function translation (line 79) | pub fn translation(x: f64, y: f64) -> Transform2D {
function scale (line 89) | pub fn scale(s: f64) -> Transform2D {
function scale_x (line 95) | pub fn scale_x(s: f64) -> Transform2D {
function scale_y (line 101) | pub fn scale_y(s: f64) -> Transform2D {
FILE: src/utils.rs
function clampf32 (line 8) | pub fn clampf32(f: f32) -> f32 {
function degrees (line 13) | pub fn degrees<F: Float + NumCast>(d: F) -> F {
function turns (line 18) | pub fn turns<F: Float + NumCast>(t: F) -> F {
function modulo (line 25) | pub fn modulo<I: Int>(a: I, b: I) -> I {
function fmod (line 34) | pub fn fmod(f: f32, n: i32) -> f32 {
function min (line 40) | pub fn min(a: f32, b: f32) -> f32 {
function max (line 45) | pub fn max(a: f32, b: f32) -> f32 {
function clamp (line 51) | pub fn clamp<T: PartialOrd>(val: T, min: T, max: T) -> T {
function map_range (line 56) | pub fn map_range<X: NumCast, Y: NumCast>
Condensed preview — 14 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (89K chars).
[
{
"path": ".gitignore",
"chars": 289,
"preview": "# RUST STUFF\n\n# Compiled files\n*.o\n*.so\n*.rlib\n*.dll\n\n# Executables\n*.exe\n\n# Generated by Cargo\n/target/\nCargo.lock\n\n\n\n#"
},
{
"path": ".travis.yml",
"chars": 951,
"preview": "sudo: false\nlanguage: rust\nos:\n- linux\nenv:\n global:\n - secure: W/pxVmgtzNXIQNPOm9lsIjSr2nEHGVD8uOGV0be4kdz0bUXCjFDe1j"
},
{
"path": "Cargo.toml",
"chars": 619,
"preview": "[package]\nname = \"elmesque\"\nversion = \"0.12.0\"\nauthors = [\"mitchmindtree <mitchell.nordine@gmail.com>\"]\ndescription = \"A"
},
{
"path": "ELM_LICENSE.md",
"chars": 1531,
"preview": "Copyright (c) 2013-2015, Evan Czaplicki\n\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with o"
},
{
"path": "README.md",
"chars": 763,
"preview": "# elmesque [](https://travis-ci.org/mitch"
},
{
"path": "assets/NotoSans/LICENSE-2.0.txt",
"chars": 11358,
"preview": "\n Apache License\n Version 2.0, January 2004\n "
},
{
"path": "examples/graphics.rs",
"chars": 4895,
"preview": "extern crate elmesque;\nextern crate find_folder;\nextern crate graphics;\nextern crate num;\nextern crate piston;\nextern cr"
},
{
"path": "src/color.rs",
"chars": 15657,
"preview": "//! \n//! A library providing simple `Color` and `Gradient` types along with useful transformations and\n//! presets.\n//!\n"
},
{
"path": "src/element.rs",
"chars": 22003,
"preview": "//! \n//! Ported from [elm-lang's `Graphics.Element` module]\n//! (https://github.com/elm-lang/core/blob/1.1.1/src/Graphic"
},
{
"path": "src/form.rs",
"chars": 16959,
"preview": "//!\n//! Ported from [elm-lang's `Graphics.Collage` module]\n//! (https://github.com/elm-lang/core/blob/62b22218c42fb8ccc9"
},
{
"path": "src/lib.rs",
"chars": 569,
"preview": "//!\n//! This crate is an attempt at porting Elm's incredibly useful std graphics modules.\n//!\n//! Visit [elm-lang.org](h"
},
{
"path": "src/text.rs",
"chars": 5269,
"preview": "\nuse color::{black, Color};\nuse std::path::PathBuf;\n\n\n/// Drawable Text.\n#[derive(Clone, Debug)]\npub struct Text {\n p"
},
{
"path": "src/transform_2d.rs",
"chars": 2722,
"preview": "//!\n//! Ported from [elm-lang's Transform2D module]\n//! (https://github.com/elm-lang/core/blob/62b22218c42fb8ccc996c86be"
},
{
"path": "src/utils.rs",
"chars": 1821,
"preview": "\nuse num::{Float, NumCast};\nuse num::PrimInt as Int;\nuse num::traits::cast;\nuse std::f32::consts::PI;\n\n/// Clamp a f32 b"
}
]
About this extraction
This page contains the full source code of the mitchmindtree/elmesque GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 14 files (83.4 KB), approximately 23.4k tokens, and a symbol index with 227 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.