Layout.jl: Graphics Layout Management for Julia

Graphical layout manager for Julia

This is intended for experiments with graphical layout management in Julia. It's not clear whether this has a long-term future as a standalone repository, or whether it will be integrated into other packages. For now, it's separate just to avoid breaking things in those other packages.

Rough sketch of "vision"

  1. Implement the "flexible core," probably based on a linear programming model such as the Auckland Layout Model. (Paper) This will support figures as vector-graphics files (svg, pdf, etc), and impose very few built-in constraints on layout. We may also want to support other optimizers besides linear programming. If they can't run quickly, they may not be suitable for real-time window resizing, but they could be used to create figures for publication.
  2. Find out whether the flexible core can be directly used for window resize events via callbacks (main targets might be Tk and HTML5/CSS). If not, then we probably need a raw representation that directly exposes the toolkit's manager, and then for SVG/PDF/etc we may need to mimic the toolkit's behavior in the context of the flexible core (yuck).
  3. Figure out how to integrate this into Compose and/or Winston.

Examples of layout

Image1 challenges:

  1. Marginal axes must be aligned with the axes of the scatterplot
  2. The image must not look squashed or stretched along any axis
  3. Image is aligned with the scatterplot
  4. Adequate space for all labels, without overlapping other elements

Status

There's a first draft of a working manager, based on linear programming. The test file "simple.jl" should run and produce reasonable results.

The LP interface is based on SymbolicLP.

Download Details:

Author: Timholy
Source Code: https://github.com/timholy/Layout.jl 
License: MIT license

#julia #graphic #layout 

Layout.jl: Graphics Layout Management for Julia

Draw3D.jl: A 3D Graphics Package with A Julian API, Built on OpenGL

Draw3D

Note - NOT STABLE, NOT USABLE

This is pre-alpha and nothing about it is stable, or sometimes even functioning.

Draw3D is a package for 3D graphics with a nice Julian API.

It is built on top of the OpenGL.jl and GLFW.jl packages and takes advantage of the fantastic work they have done wrapping the OpenGL API. Those packages provide an API that is a direct mapping of the OpenGL C API however, which isn't particularly user-friendly if you are not already an OpenGL developer.

The main datatypes are Mesh, Light, Material, Transform

Examples

The simplest possible thing is to render a unit (1x1x1) cube in the default position, which is with its local origin at the global origin.

c = Cube()
display(c)

To make a cube that is twice as large and centered at the origin:

c = Cube()
c = scale(c, 2)
c = translate(c, -1, -1, -1)
display(c)

As you can see this can get a little verbose. All the transform functions are also defined for partial application, so you can also use Julia's built-in composition operator like so:

Cube() |> scale(2) |> translate(-1, -1, -1) |> display

Limitations

Transparent Materials

Transparent objects are assumed to be convex, and are rendered by first rendering the faces pointing away from the camera and then those pointing towards. Handling this properly for non-convex meshes requires z-sorting the faces, which we don't do right now.

Dependencies

  • GLFW.jl
  • OpenGL.jl
  • libglfw2

Download Details:

Author: ssfrr
Source Code: https://github.com/ssfrr/Draw3D.jl 
License: View license

#julia #opengl #graphic 

Draw3D.jl: A 3D Graphics Package with A Julian API, Built on OpenGL

Julia Package for Click Maps & Advanced interaction with Graphics

Click!

Julia package to allow simpler interaction with graphics

Warning

Large portions of this package still need testing so not everything is guaranteed to work.

Image Maps

The simplest application of this library is to create a click map for a static image that is loaded into a GUI. Create the image map and connect it to the underlying GUI using this package as seen below for Gtk.

This code (example/gtk_ex2.jl) creates a window with a square in the center of the window that opens an info dialog when clicked.

#!/usr/bin/env julia
using Click, Click.Primitives, Gtk, Graphics

# create a 200x200 canvas in a window
canvas = @GtkCanvas(200, 200)
win = @GtkWindow(canvas, "Gtk Example")

# Setup drawing the rectangle
@guarded draw(canvas) do c
  ctx = getgc(c)
  set_source_rgb(ctx, 1.0, 0.0, 0.0)
  rectangle(ctx, 50, 50, 100, 100)
  fill(ctx)
end

# create a 100x100 clickable rectangle
rect = Rectangle(50, 50, 100, 100)
clickRect = ClickableRectangle(rect)

# Attach a callback to a click event
attend(clickRect, :click) do widget
  global win
  info_dialog("Clicked the rectangle", win)
end

# create a ClickMap containing the clickable rectangle
m = SimpleClickMap(clickRect)

# link the click map to the canvas
link_click_map(canvas, m)

# Display the result
show(canvas)

# Pause main thread execution in non-interactive mode
if !isinteractive()
  signal_connect(win, :destroy) do widget
    Gtk.gtk_quit()
  end
  Gtk.gtk_main()
end

Using GTK and Compose

A very useful case is to make objects from a library, such as Compose*, which abstracts the drawing process, clickable. This example (example/gtk_compose_ex.jl) demonstrates this by allowing essentially the same square to be clicked at both locations at which it is drawn. It also hides certain implementation details such as Gtk's drawing loop.

#!/usr/bin/env julia

using Gtk, Compose, Click

# Create a 400x400 canvas
canvas = @GtkCanvas(400, 400)
win = @GtkWindow(canvas, "GTK-Compose Example")

rect = rectangle(0.25, 0.25, 0.5, 0.5)
vect = compose(context(0mm, 0mm, 300mm, 300mm),
         rectangle(0.0, 0.0, 1.0, 1.0),
         fill("green"),
         compose(context(0.0, 0.0, 0.5, 0.5),
           rect,
           fill("black")),
         compose(context(0.5, 0.5, 0.5, 0.5),
           rect,
           fill("blue")))

cl = create_clickable(rect, vect)

attend(cl, :click) do frm, x, y
  info_dialog("Clicked Square", win)
end

# Use the fuse function to stitch things together

# ComposeClickMap needs to wrap SimpleClickMap to account for pixel density
# which that object is updated with internal to fuse
fuse(SimpleClickMap(cl), canvas, vect)

show(canvas)

# block the main thread of execution if not interactive
if !isinteractive()
  signal_connect(win, :destroy) do widget
    Gtk.gtk_quit()
  end
  Gtk.gtk_main()
end

* Be careful with this at the moment as Compose support is a work in progress.

Download Details:

Author: Matt5sean3
Source Code: https://github.com/Matt5sean3/Click.jl 

#julia #maps #graphic 

Julia Package for Click Maps & Advanced interaction with Graphics
Royce  Reinger

Royce Reinger

1664264899

10 Popular Graphics Library for Rust

In today's post we will learn about 10 Popular Graphics Library for Rust.

What is a Graphic?

In general, the term graphic refers to a design or visual image displayed on a variety of surfaces, including canvas, paper, walls, signs, or a computer monitor. They are created for entertainment, branding, or providing information.

CG or computer graphics are any image media, usually movies and pictures that are created through the use of hardware and software. They are often referred to as computer-generated imagery; more commonly known as CGI. An example of a computer graphic is the picture shown on this page.

Graphic may also be used to describe a phrase, gesture, or image that is considered obscene or offensive.

Table of contents:

  • RazrFalcon/rustybuzz - An incremental harfbuzz port to Rust.
  • Redox-os/rusttype - A pure Rust alternative to libraries like FreeType.
  • Gfx-rs/gfx - A high-performance, bindless graphics API for Rust. 
  • Gfx-rs/wgpu - Native WebGPU implementation based on gfx-hal. 
  • Brendanzab/gl-rs - An OpenGL function pointer loader for Rust.
  • Glium/glium - Safe OpenGL wrapper for the Rust language. 
  • PistonDevelopers/glfw-rs - GLFW3 bindings and idiomatic wrapper for Rust.
  • FSchutt/printpdf - PDF writing library.
  • J-F-Liu/lopdf - PDF document manipulation.
  • Kaj/rust-pdf - Generating PDF files in pure Rust.

1 - RazrFalcon/rustybuzz:

An incremental harfbuzz port to Rust.

rustybuzz is a complete harfbuzz's shaping algorithm port to Rust.

Matches harfbuzz v2.7.1

Why?

Because you can add rustybuzz = "*" to your project and it just works. No need for a C++ compiler. No need to configure anything. No need to link to system libraries.

Conformance

rustybuzz passes 98% of harfbuzz tests (1764 to be more precise). So it's mostly identical, but there are still some tiny edge-cases which are not implemented yet or cannot be implemented at all.

Also, Apple layout is largely untested, because we cannot include Apple fonts for legal reasons. harfbuzz uses macOS CI instances to test it, which is extremely painful and we do not do this for now.

Major changes

  • Subsetting removed.
  • TrueType parsing is completely handled by the ttf-parser. And while the parsing algorithm is very different, it's not better or worse, just different.
  • Malformed fonts will cause an error. HarfBuzz uses fallback/dummy shaper in this case.
  • No font size property. Shaping is always using UnitsPerEm. You should scale the result manually.
  • Most of the TrueType and Unicode handling code was moved into separate crates.
  • rustybuzz doesn't interact with any system libraries and must produce exactly the same results on all OS'es and targets.
  • mort table is not supported, since it's deprecated by Apple.
  • No Arabic fallback shaper, since it requires subsetting.
  • No graphite library support.
  • No automated Apple layout testing for legal reasons. We just cannot include Apple fonts. harfbuzz avoids this by running such tests only on CI, which is far from ideal.

Lines of code

As mentioned above, rustybuzz has around 20 KLOC. But this is not strictly true, because there are a lot of auto-generated data tables.

You can find the "real" code size using:

tokei --exclude unicode_norm.rs --exclude complex/vowel_constraints.rs \
      --exclude '*_machine.rs' --exclude '*_table.rs' src

Which gives us around 13 KLOC, which is still a lot.

Future work

Since the port is finished, there is not much to do other than syncing it with a new harfbuzz releases. But there are still a lot of room for performance optimizations and refactoring.

Also, despite the fact that harfbuzz has a vast test suite, there are still a lot of things left to test.

Safety

The library is completely safe.

We do have one unsafe to cast between two POD structures, which is perfectly safe. But except that, there are no unsafe in this library and in most of its dependencies (excluding bytemuck).

View on Github

2 - Redox-os/rusttype:

A pure Rust alternative to libraries like FreeType.

The current capabilities of RustType:

  • Reading OpenType formatted fonts and font collections. This includes *.ttf as well as *.otf font files.
  • Retrieving glyph shapes and commonly used properties for a font and its glyphs.
  • Laying out glyphs horizontally using horizontal and vertical metrics, and glyph-pair-specific kerning.
  • Rasterising glyphs with sub-pixel positioning using an accurate analytical algorithm (not based on sampling).
  • Managing a font cache on the GPU with the gpu_cache module. This keeps recently used glyph renderings in a dynamic cache in GPU memory to minimise texture uploads per-frame. It also allows you keep the draw call count for text very low, as all glyphs are kept in one GPU texture.

Notable things that RustType does not support yet:

  • Font hinting.
  • Ligatures of any kind.
  • Some less common TrueType sub-formats.
  • Right-to-left and vertical text layout.

Testing & examples

Heavier examples, tests & benchmarks are in the ./dev directory. This avoids dev-dependency feature bleed.

Run all tests with cargo test --all --all-features.

Run examples with cargo run --example <NAME> -p dev

Getting Started

To hit the ground running with RustType, look at dev/examples/ascii.rs supplied with the crate. It demonstrates loading a font file, rasterising an arbitrary string, and displaying the result as ASCII art. If you prefer to just look at the documentation, the entry point for loading fonts is Font, from which you can access individual fonts, then their glyphs.

Future Plans

The initial motivation for the project was to provide easy-to-use font rendering for games. There are numerous avenues for improving RustType. Ideas:

  • Support for some common forms of ligatures.
  • And, eventually, support for embedded right-to-left Unicode text.

If you think you could help with achieving any of these goals, feel free to open a tracking issue for discussing them.

View on Github

3 - Gfx-rs/gfx:

A high-performance, bindless graphics API for Rust.

gfx-rs is a low-level, cross-platform graphics and compute abstraction library in Rust. It consists of the following components:

gfx-hal deprecation

As of the v0.9 release, gfx-hal is now in maintenance mode. gfx-hal development was mainly driven by wgpu, which has now switched to its own GPU abstraction called wgpu-hal. For this reason, gfx-hal development has switched to maintenance only, until the developers figure out the story for gfx-portability. Read more about the transition in #3768.

hal

  • gfx-hal which is gfx's hardware abstraction layer: a Vulkan-ic mostly unsafe API which translates to native graphics backends.
  • gfx-backend-* which contains graphics backends for various platforms:
  • gfx-warden which is a data-driven reference test framework, used to verify consistency across all graphics backends.

gfx-rs is hard to use, it's recommended for performance-sensitive libraries and engines. If that's not your domain, take a look at wgpu-rs for a safe and simple alternative.

Hardware Abstraction Layer

The Hardware Abstraction Layer (HAL), is a thin, low-level graphics and compute layer which translates API calls to various backends, which allows for cross-platform support. The API of this layer is based on the Vulkan API, adapted to be more Rust-friendly.

Hardware Abstraction Layer (HAL)

Currently HAL has backends for Vulkan, DirectX 12/11, Metal, and OpenGL/OpenGL ES/WebGL.

The HAL layer is consumed directly by user applications or libraries. HAL is also used in efforts such as gfx-portability.

See the Big Picture blog post for connections.

The old gfx crate (pre-ll)

This repository was originally home to the gfx crate, which is now deprecated. You can find the latest versions of the code for that crate in the pre-ll branch of this repository.

The master branch of this repository is now focused on developing gfx-hal and its associated backend and helper libraries, as described above. gfx-hal is a complete rewrite of gfx, but it is not necessarily the direct successor to gfx. Instead, it serves a different purpose than the original gfx crate, by being "lower level" than the original. Hence, the name of gfx-hal was originally ll, which stands for "lower level", and the original gfx is now referred to as pre-ll.

The spiritual successor to the original gfx is actually wgpu, which stands on a similar level of abstraction to the old gfx crate, but with a modernized API that is more fit for being used over Vulkan/DX12/Metal. If you want something similar to the old gfx crate that is being actively developed, wgpu is probably what you're looking for, rather than gfx-hal.

View on Github

4 - Gfx-rs/wgpu:

Native WebGPU implementation based on gfx-hal.

wgpu is a cross-platform, safe, pure-rust graphics api. It runs natively on Vulkan, Metal, D3D12, D3D11, and OpenGLES; and on top of WebGPU on wasm.

The api is based on the WebGPU standard. It serves as the core of the WebGPU integration in Firefox, Servo, and Deno.

MSRV policy

Minimum Supported Rust Version is 1.59. It is enforced on CI (in "/.github/workflows/ci.yml") with RUST_VERSION variable. This version can only be upgraded in breaking releases.

Getting Started

Rust

Rust examples can be found at wgpu/examples. You can run the examples with cargo run --example name. See the list of examples. For detailed instructions, look at our Get Started wiki.

If you are looking for a wgpu tutorial, look at the following:

C/C++

To use wgpu in C/C++, you need wgpu-native.

Others

If you want to use wgpu in other languages, there are many bindings to wgpu-native from languages such as Python, D, Julia, Kotlin, and more. See the list.

Core Test Infrastructure

We use a tool called cargo nextest to run our tests. To install it, run cargo install cargo-nextest.

To run the test suite on the default device:

cargo nextest run --no-fail-fast

wgpu-info can run the tests once for each adapter on your system.

cargo run --bin wgpu-info -- cargo nextest run --no-fail-fast

Then to run an example's image comparison tests, run:

cargo nextest run <example-test-name> --no-fail-fast

Or run a part of the integration test suite:

cargo nextest run -p wgpu -- <name-of-test>

If you are a user and want a way to help contribute to wgpu, we always need more help writing test cases.

WebGPU Conformance Test Suite

WebGPU includes a Conformance Test Suite to validate that implementations are working correctly. We can run this CTS against wgpu.

To run the CTS, first you need to check it out:

git clone https://github.com/gpuweb/cts.git
cd cts
# works in bash and powershell
git checkout $(cat ../cts_runner/revision.txt)

To run a given set of tests:

# Must be inside the cts folder we just checked out, else this will fail
cargo run --manifest-path ../cts_runner/Cargo.toml -- ./tools/run_deno --verbose "<test string>"

To find the full list of tests, go to the online cts viewer.

The list of currently enabled CTS tests can be found here.

View on Github

5 - Brendanzab/gl-rs:

An OpenGL function pointer loader for Rust.

Overview

This repository contains the necessary building blocks for OpenGL wrapper libraries. For more information on each crate, see their respective READMEs listed below.

The following crates are contained in this repository:

gl

README

An OpenGL function pointer loader for the Rust Programming Language.

[dependencies]
gl = "0.14.0"

gl_generator

README

Code generators for creating bindings to the Khronos OpenGL APIs.

[build-dependencies]
gl_generator = "0.14.0"

khronos_api

README

The Khronos XML API Registry, exposed as byte string constants.

[build-dependencies]
khronos_api = "3.1.0"

Compiling from source

khronos_api makes use of git submodules. You will need to initialize these before building:

git submodule update --init

webgl_generator

README

Code generators for creating bindings to the WebGL APIs.

[build-dependencies]
webgl_generator = "0.2.0"

View on Github

6 - Glium/glium:

Safe OpenGL wrapper for the Rust language.

Note to current and future Glium users:

Glium is no longer actively developed by its original author. That said, PRs are still welcome and maintenance is continued by the surrounding community.

Elegant and safe OpenGL wrapper.

Glium is an intermediate layer between OpenGL and your application. You still need to manually handle the graphics pipeline, but without having to use OpenGL's old and error-prone API.

[dependencies] glium = "*"

Its objectives:

  • Be safe to use. Many aspects of OpenGL that can trigger a crash if misused are automatically handled by glium.
  • Provide an API that enforces good practices such as RAII or stateless function calls.
  • Be compatible with all OpenGL versions that support shaders, providing a unified API when things diverge.
  • Avoid all OpenGL errors beforehand.
  • Produce optimized OpenGL function calls, and allow the user to easily use modern OpenGL techniques.

Why should I use Glium instead of raw OpenGL calls?

Easy to use:

Functions are higher level in glium than in OpenGL. Glium's API tries to be as Rusty as possible, and shouldn't be much different than using any other Rust library. Glium should allow you to do everything that OpenGL allows you to do, just through high-level functions. If something is missing, please open an issue.

You can directly pass vectors, matrices and images to glium instead of manipulating low-level data.

Thanks to glutin, glium is very easy to setup compared to raw OpenGL.

Glium provides easier ways to do common tasks. For example the VertexBuffer struct contains information about the vertex bindings, because you usually don't use several different bindings with the same vertex buffer. This reduces the overall complexity of OpenGL.

Glium handles framebuffer objects, samplers, and vertex array objects for you. You no longer need to create them explicitly as they are automatically created when needed and destroyed when their corresponding object is destroyed.

Glium is stateless. There are no set_something() functions in the entire library, and everything is done by parameter passing. The same set of function calls will always produce the same results, which greatly reduces the number of potential problems.

Safety:

Glium detects what would normally be errors or undefined behaviors in OpenGL, and panics, without calling glGetError which would be too slow. Examples include requesting a depth test when you don't have a depth buffer available, not binding any value to an attribute or uniform, or binding multiple textures with different dimensions to the same framebuffer.

If the OpenGL context triggers an error, then you have found a bug in glium. Please open an issue. Just like Rust does everything it can to avoid crashes, glium does everything it can to avoid OpenGL errors.

The OpenGL context is automatically handled by glium. You don't need to worry about thread safety, as it is forbidden to change the thread in which OpenGL objects operate. Glium also allows you to safely replace the current OpenGL context with another one that shares the same lists.

Glium enforces RAII. Creating a Texture2d struct creates a texture, and destroying the struct destroys the texture. It also uses Rust's borrow system to ensure that objects are still alive and in the right state when you use them. Glium provides the same guarantees with OpenGL objects that you have with regular objects in Rust.

High-level functions are much easier to use and thus less error-prone. For example there is no risk of making a mistake while specifying the names and offsets of your vertex attributes, since Glium automatically generates this data for you.

Robustness is automatically handled. If the OpenGL context is lost (because of a crash in the driver for example) then swapping buffers will return an error.

View on Github

7 - PistonDevelopers/glfw-rs:

GLFW3 bindings and idiomatic wrapper for Rust.

Example

extern crate glfw;

use glfw::{Action, Context, Key};

fn main() {
    let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();

    let (mut window, events) = glfw.create_window(300, 300, "Hello this is window", glfw::WindowMode::Windowed)
        .expect("Failed to create GLFW window.");

    window.set_key_polling(true);
    window.make_current();

    while !window.should_close() {
        glfw.poll_events();
        for (_, event) in glfw::flush_messages(&events) {
            handle_window_event(&mut window, event);
        }
    }
}

fn handle_window_event(window: &mut glfw::Window, event: glfw::WindowEvent) {
    match event {
        glfw::WindowEvent::Key(Key::Escape, _, Action::Press, _) => {
            window.set_should_close(true)
        }
        _ => {}
    }
}

Using glfw-rs

Prerequisites

Make sure you have compiled and installed GLFW 3.x. You might be able to find it on your package manager, for example on OS X: brew install glfw3 (you may need to run brew tap homebrew/versions). If not you can download and build the library from the source supplied on the GLFW website. Note that if you compile GLFW with CMake on Linux, you will have to supply the -DCMAKE_C_FLAGS=-fPIC argument. You may install GLFW to your PATH, otherwise you will have to specify the directory containing the library binaries when you call make or make lib:

GLFW_LIB_DIR=path/to/glfw/lib/directory make

Including glfw-rs in your project

Add this to your Cargo.toml:

[dependencies.glfw]
git = "https://github.com/bjz/glfw-rs.git"

On Windows

By default, glfw-rs will try to compile the glfw library. If you want to link to your custom build of glfw or if the build doesn't work (which is probably the case on Windows), you can disable this:

[dependencies.glfw]
git = "https://github.com/bjz/glfw-rs.git"
default-features = false

View on Github

8 - FSchutt/printpdf:

PDF writing library.

printpdf is a library designed for creating printable PDF documents.

[dependencies]
printpdf = "0.5.0"

Features

Currently, printpdf can only write documents, not read them.

  • Page generation
  • Layers (Illustrator like layers)
  • Graphics (lines, shapes, bezier curves)
  • Images (currently BMP/PNG/JPG only or generate your own images)
  • Embedded fonts (TTF and OTF) with Unicode support
  • Advanced graphics - overprint control, blending modes, etc.
  • Advanced typography - character scaling, character spacing, superscript, subscript, outlining, etc.
  • PDF layers (you should be able to open the PDF in Illustrator and have the layers appear)

Getting started

Writing PDF

Simple page

use printpdf::*;
use std::fs::File;
use std::io::BufWriter;

let (doc, page1, layer1) = PdfDocument::new("PDF_Document_title", Mm(247.0), Mm(210.0), "Layer 1");
let (page2, layer1) = doc.add_page(Mm(10.0), Mm(250.0),"Page 2, Layer 1");

doc.save(&mut BufWriter::new(File::create("test_working.pdf").unwrap())).unwrap();

Adding graphical shapes

use printpdf::*;
use std::fs::File;
use std::io::BufWriter;
use std::iter::FromIterator;

let (doc, page1, layer1) = PdfDocument::new("printpdf graphics test", Mm(297.0), Mm(210.0), "Layer 1");
let current_layer = doc.get_page(page1).get_layer(layer1);

// Quadratic shape. The "false" determines if the next (following)
// point is a bezier handle (for curves)
// If you want holes, simply reorder the winding of the points to be
// counterclockwise instead of clockwise.
let points1 = vec![(Point::new(Mm(100.0), Mm(100.0)), false),
                   (Point::new(Mm(100.0), Mm(200.0)), false),
                   (Point::new(Mm(300.0), Mm(200.0)), false),
                   (Point::new(Mm(300.0), Mm(100.0)), false)];

// Is the shape stroked? Is the shape closed? Is the shape filled?
let line1 = Line {
    points: points1,
    is_closed: true,
    has_fill: true,
    has_stroke: true,
    is_clipping_path: false,
};

// Triangle shape
// Note: Line is invisible by default, the previous method of
// constructing a line is recommended!
let mut line2 = Line::from_iter(vec![
    (Point::new(Mm(150.0), Mm(150.0)), false),
    (Point::new(Mm(150.0), Mm(250.0)), false),
    (Point::new(Mm(350.0), Mm(250.0)), false)]);

line2.set_stroke(true);
line2.set_closed(false);
line2.set_fill(false);
line2.set_as_clipping_path(false);

let fill_color = Color::Cmyk(Cmyk::new(0.0, 0.23, 0.0, 0.0, None));
let outline_color = Color::Rgb(Rgb::new(0.75, 1.0, 0.64, None));
let mut dash_pattern = LineDashPattern::default();
dash_pattern.dash_1 = Some(20);

current_layer.set_fill_color(fill_color);
current_layer.set_outline_color(outline_color);
current_layer.set_outline_thickness(10.0);

// Draw first line
current_layer.add_shape(line1);

let fill_color_2 = Color::Cmyk(Cmyk::new(0.0, 0.0, 0.0, 0.0, None));
let outline_color_2 = Color::Greyscale(Greyscale::new(0.45, None));

// More advanced graphical options
current_layer.set_overprint_stroke(true);
current_layer.set_blend_mode(BlendMode::Seperable(SeperableBlendMode::Multiply));
current_layer.set_line_dash_pattern(dash_pattern);
current_layer.set_line_cap_style(LineCapStyle::Round);

current_layer.set_fill_color(fill_color_2);
current_layer.set_outline_color(outline_color_2);
current_layer.set_outline_thickness(15.0);

// draw second line
current_layer.add_shape(line2);

View on Github

9 - J-F-Liu/lopdf:

PDF document manipulation.

Example Code

  • Create PDF document
use lopdf::dictionary;
use lopdf::{Document, Object, Stream};
use lopdf::content::{Content, Operation};

let mut doc = Document::with_version("1.5");
let pages_id = doc.new_object_id();
let font_id = doc.add_object(dictionary! {
    "Type" => "Font",
    "Subtype" => "Type1",
    "BaseFont" => "Courier",
});
let resources_id = doc.add_object(dictionary! {
    "Font" => dictionary! {
        "F1" => font_id,
    },
});
let content = Content {
    operations: vec![
        Operation::new("BT", vec![]),
        Operation::new("Tf", vec!["F1".into(), 48.into()]),
        Operation::new("Td", vec![100.into(), 600.into()]),
        Operation::new("Tj", vec![Object::string_literal("Hello World!")]),
        Operation::new("ET", vec![]),
    ],
};
let content_id = doc.add_object(Stream::new(dictionary! {}, content.encode().unwrap()));
let page_id = doc.add_object(dictionary! {
    "Type" => "Page",
    "Parent" => pages_id,
    "Contents" => content_id,
});
let pages = dictionary! {
    "Type" => "Pages",
    "Kids" => vec![page_id.into()],
    "Count" => 1,
    "Resources" => resources_id,
    "MediaBox" => vec![0.into(), 0.into(), 595.into(), 842.into()],
};
doc.objects.insert(pages_id, Object::Dictionary(pages));
let catalog_id = doc.add_object(dictionary! {
    "Type" => "Catalog",
    "Pages" => pages_id,
});
doc.trailer.set("Root", catalog_id);
doc.compress();

// Store file in current working directory.
// Note: Line is exclude for when running tests
if false {
    doc.save("example.pdf").unwrap();
}
  • Merge PDF documents
use lopdf::dictionary;

use std::collections::BTreeMap;

use lopdf::content::{Content, Operation};
use lopdf::{Document, Object, ObjectId, Stream, Bookmark};

pub fn generate_fake_document() -> Document {
    let mut doc = Document::with_version("1.5");
    let pages_id = doc.new_object_id();
    let font_id = doc.add_object(dictionary! {
        "Type" => "Font",
        "Subtype" => "Type1",
        "BaseFont" => "Courier",
    });
    let resources_id = doc.add_object(dictionary! {
        "Font" => dictionary! {
            "F1" => font_id,
        },
    });
    let content = Content {
        operations: vec![
            Operation::new("BT", vec![]),
            Operation::new("Tf", vec!["F1".into(), 48.into()]),
            Operation::new("Td", vec![100.into(), 600.into()]),
            Operation::new("Tj", vec![Object::string_literal("Hello World!")]),
            Operation::new("ET", vec![]),
        ],
    };
    let content_id = doc.add_object(Stream::new(dictionary! {}, content.encode().unwrap()));
    let page_id = doc.add_object(dictionary! {
        "Type" => "Page",
        "Parent" => pages_id,
        "Contents" => content_id,
        "Resources" => resources_id,
        "MediaBox" => vec![0.into(), 0.into(), 595.into(), 842.into()],
    });
    let pages = dictionary! {
        "Type" => "Pages",
        "Kids" => vec![page_id.into()],
        "Count" => 1,
    };
    doc.objects.insert(pages_id, Object::Dictionary(pages));
    let catalog_id = doc.add_object(dictionary! {
        "Type" => "Catalog",
        "Pages" => pages_id,
    });
    doc.trailer.set("Root", catalog_id);

    doc
}

fn main() -> std::io::Result<()> {
    // Generate a stack of Documents to merge
    let documents = vec![
        generate_fake_document(),
        generate_fake_document(),
        generate_fake_document(),
        generate_fake_document(),
    ];

    // Define a starting max_id (will be used as start index for object_ids)
    let mut max_id = 1;
    let mut pagenum = 1;
    // Collect all Documents Objects grouped by a map
    let mut documents_pages = BTreeMap::new();
    let mut documents_objects = BTreeMap::new();
    let mut document = Document::with_version("1.5");

    for mut doc in documents {
        let mut first = false;
        doc.renumber_objects_with(max_id);

        max_id = doc.max_id + 1;

        documents_pages.extend(
            doc
                    .get_pages()
                    .into_iter()
                    .map(|(_, object_id)| {
                        if !first {
                            let bookmark = Bookmark::new(String::from(format!("Page_{}", pagenum)), [0.0, 0.0, 1.0], 0, object_id);
                            document.add_bookmark(bookmark, None);
                            first = true;
                            pagenum += 1;
                        }

                        (
                            object_id,
                            doc.get_object(object_id).unwrap().to_owned(),
                        )
                    })
                    .collect::<BTreeMap<ObjectId, Object>>(),
        );
        documents_objects.extend(doc.objects);
    }

    // Catalog and Pages are mandatory
    let mut catalog_object: Option<(ObjectId, Object)> = None;
    let mut pages_object: Option<(ObjectId, Object)> = None;

    // Process all objects except "Page" type
    for (object_id, object) in documents_objects.iter() {
        // We have to ignore "Page" (as are processed later), "Outlines" and "Outline" objects
        // All other objects should be collected and inserted into the main Document
        match object.type_name().unwrap_or("") {
            "Catalog" => {
                // Collect a first "Catalog" object and use it for the future "Pages"
                catalog_object = Some((
                    if let Some((id, _)) = catalog_object {
                        id
                    } else {
                        *object_id
                    },
                    object.clone(),
                ));
            }
            "Pages" => {
                // Collect and update a first "Pages" object and use it for the future "Catalog"
                // We have also to merge all dictionaries of the old and the new "Pages" object
                if let Ok(dictionary) = object.as_dict() {
                    let mut dictionary = dictionary.clone();
                    if let Some((_, ref object)) = pages_object {
                        if let Ok(old_dictionary) = object.as_dict() {
                            dictionary.extend(old_dictionary);
                        }
                    }

                    pages_object = Some((
                        if let Some((id, _)) = pages_object {
                            id
                        } else {
                            *object_id
                        },
                        Object::Dictionary(dictionary),
                    ));
                }
            }
            "Page" => {}     // Ignored, processed later and separately
            "Outlines" => {} // Ignored, not supported yet
            "Outline" => {}  // Ignored, not supported yet
            _ => {
                document.objects.insert(*object_id, object.clone());
            }
        }
    }

    // If no "Pages" found abort
    if pages_object.is_none() {
        println!("Pages root not found.");

        return Ok(());
    }

    // Iter over all "Page" and collect with the parent "Pages" created before
    for (object_id, object) in documents_pages.iter() {
        if let Ok(dictionary) = object.as_dict() {
            let mut dictionary = dictionary.clone();
            dictionary.set("Parent", pages_object.as_ref().unwrap().0);

            document
                    .objects
                    .insert(*object_id, Object::Dictionary(dictionary));
        }
    }

    // If no "Catalog" found abort
    if catalog_object.is_none() {
        println!("Catalog root not found.");

        return Ok(());
    }

    let catalog_object = catalog_object.unwrap();
    let pages_object = pages_object.unwrap();

    // Build a new "Pages" with updated fields
    if let Ok(dictionary) = pages_object.1.as_dict() {
        let mut dictionary = dictionary.clone();

        // Set new pages count
        dictionary.set("Count", documents_pages.len() as u32);

        // Set new "Kids" list (collected from documents pages) for "Pages"
        dictionary.set(
            "Kids",
            documents_pages
                    .into_iter()
                    .map(|(object_id, _)| Object::Reference(object_id))
                    .collect::<Vec<_>>(),
        );

        document
                .objects
                .insert(pages_object.0, Object::Dictionary(dictionary));
    }

    // Build a new "Catalog" with updated fields
    if let Ok(dictionary) = catalog_object.1.as_dict() {
        let mut dictionary = dictionary.clone();
        dictionary.set("Pages", pages_object.0);
        dictionary.remove(b"Outlines"); // Outlines not supported in merged PDFs

        document
                .objects
                .insert(catalog_object.0, Object::Dictionary(dictionary));
    }

    document.trailer.set("Root", catalog_object.0);

    // Update the max internal ID as wasn't updated before due to direct objects insertion
    document.max_id = document.objects.len() as u32;

    // Reorder all new Document objects
    document.renumber_objects();

     //Set any Bookmarks to the First child if they are not set to a page
    document.adjust_zero_pages();

    //Set all bookmarks to the PDF Object tree then set the Outlines to the Bookmark content map.
    if let Some(n) = document.build_outline() {
        if let Ok(x) = document.get_object_mut(catalog_object.0) {
            if let Object::Dictionary(ref mut dict) = x {
                dict.set("Outlines", Object::Reference(n));
            }
        }
    }

    document.compress();

    // Save the merged PDF
    // Store file in current working directory.
    // Note: Line is exclude for when running tests
    if false {
        document.save("merged.pdf").unwrap();
    }

    Ok(())
}

View on Github

10 - Kaj/rust-pdf:

Generating PDF files in pure Rust.

A pure rust library for generating PDF files. Currently, simple vector graphics and text set in the 14 built-in fonts are supported.  

To use this library, add it as a dependency in your Cargo.toml:

[dependencies]
pdf-canvas = "*"

The API is still very alpha, usage may change. Some examples, that should work with the version containing them, can be found in the examples directory. Read the API documentation for the pdf-canvas crate. A larger example of a program using this crate is chord3, a chopro formatter.

View on Github

Thank you for following this article.

Related videos:

Intro to Rust-lang (Games and Graphics with Piston; Pong game)

#rust #graphic 

10 Popular Graphics Library for Rust
Nat  Grady

Nat Grady

1661522114

Htmlwidget interface to The MetricsGraphics.js D3 Chart Library

metricsgraphics

metricsgraphics is an 'htmlwidget' interface to the MetricsGraphics.js D3-based charting library.

Charts look best in a Boostrap page (unless you customize your own CSS).

The following functions are implemented:

  • mjs_plot: Create a new metricsgraphics.js plot
  • mjs_line: metricsgraphics.js linechart "geom"
  • mjs_add_line: used to add additional columns for a multi-line chart
  • mjs_hist: Shortcut for plotting MetricsGraphics histograms
  • mjs_histogram: Plot Histograms with MetrisGraphics
  • mjs_add_legend: adds a legend to a line (or mult-line) chart
  • mjs_point: metricsgraphics.js scatterplot "geom"
  • mjs_bar: metricsgraphics.js bar chart "geom"
  • mjs_axis_x: Configure x axis ticks & limits
  • mjs_axis_y: Configure y axis ticks & limits
  • mjs_labs: Configure axis labels & plot description
  • mjs_add_baseline: Sets a baseline line/label
  • mjs_add_marker: Sets a marker line/label
  • mjs_grid: grid.arrange-like functionality for metricsgraphics charts
  • mjs_add_mouseover: provides support for MetricsGraphics custom rollovers
  • mjs_add_confidence_band: provides support for confidence bands
  • mjs_annotate_region: Region annotations for line charts [EXPERIMENTAL]
  • mjs_add_css_rule: Add a CSS rule to the rendered htmlwidget

Installation

# stable
install.packages("metricsgraphics")
# development
# devtools::install_github("hrbrmstr/metricsgraphics")

Usage

library(metricsgraphics)
library(RColorBrewer)

tmp <- data.frame(year=seq(1790, 1970, 10), uspop=as.numeric(uspop))

tmp %>%
  mjs_plot(x=year, y=uspop) %>%
  mjs_line() %>%
  mjs_add_marker(1850, "Something Wonderful") %>%
  mjs_add_baseline(150, "Something Awful")


tmp %>%
  mjs_plot(x=year, y=uspop, width=600) %>%
  mjs_line(area=TRUE)

tmp %>%
  mjs_plot(x=uspop, y=year, width=500, height=400) %>%
  mjs_bar() %>%
  mjs_axis_x(xax_format = 'plain')


mtcars %>%
  mjs_plot(x=wt, y=mpg, width=600, height=500) %>%
  mjs_point(color_accessor=carb, size_accessor=carb) %>%
  mjs_labs(x="Weight of Car", y="Miles per Gallon")


mtcars %>%
  mjs_plot(x=wt, y=mpg, width=600, height=500) %>%
  mjs_point(color_accessor=cyl,
            x_rug=TRUE, y_rug=TRUE,
            size_accessor=carb,
            size_range=c(5, 10),
            color_type="category",
            color_range=brewer.pal(n=11, name="RdBu")[c(1, 5, 11)]) %>%
  mjs_labs(x="Weight of Car", y="Miles per Gallon")


mtcars %>%
  mjs_plot(x=wt, y=mpg, width=400, height=300) %>%
  mjs_point(least_squares=TRUE) %>%
  mjs_labs(x="Weight of Car", y="Miles per Gallon")


set.seed(1492)
dat <- data.frame(date=seq(as.Date("2014-01-01"),
                           as.Date("2014-01-31"),
                           by="1 day"),
                  value=rnorm(n=31, mean=0, sd=2))

dat %>%
  mjs_plot(x=date, y=value) %>%
  mjs_line() %>%
  mjs_axis_x(xax_format = "date")

# Custom rollovers

dat %>%
  mjs_plot(x=date, y=value) %>%
  mjs_line() %>%
  mjs_axis_x(xax_format = "date") %>%
  mjs_add_mouseover("function(d, i) {
                $('{{ID}} svg .mg-active-datapoint')
                    .text('custom text : ' + d.date + ' ' + i);
                 }")

# also works for scatterplots with a slight mod

set.seed(1492)
dat <- data.frame(value=rnorm(n=30, mean=5, sd=1),
                 value2=rnorm(n=30, mean=4, sd=1),
                 test = c(rep(c('test', 'test2'), 15)))
dat %>%
 mjs_plot(x = value, y = value2) %>%
 mjs_point() %>%
 mjs_add_mouseover("function(d, i) {
               $('{{ID}} svg .mg-active-datapoint')
                   .text('custom text : ' + d.point.test + ' ' + i);
                }")

set.seed(1492)
stocks <- data.frame(
  time = as.Date('2009-01-01') + 0:9,
  X = rnorm(10, 0, 1),
  Y = rnorm(10, 0, 2),
  Z = rnorm(10, 0, 4))

stocks %>%
  mjs_plot(x=time, y=X) %>%
  mjs_line() %>%
  mjs_axis_x(show=FALSE) %>%
  mjs_axis_y(show=FALSE)

stocks %>%
  mjs_plot(x=time, y=X) %>%
  mjs_line() %>%
  mjs_add_line(Y) %>%
  mjs_add_line(Z) %>%
  mjs_axis_x(xax_format="date")

mjs_plot(rnorm(10000)) %>%
  mjs_histogram(bins=30, bar_margin=1)

movies <- ggplot2movies::movies[sample(nrow(ggplot2movies::movies), 1000), ]

mjs_plot(movies$rating) %>% mjs_histogram()

mjs_plot(movies, rating) %>% 
  mjs_histogram() %>% 
  mjs_labs(x_label="Histogram of movie ratings", 
           y_label="Frequency")

mjs_plot(movies$rating) %>% mjs_histogram(bins=30)

mjs_plot(runif(10000)) %>% 
  mjs_labs(x_label="runif(10000)") %>%
  mjs_histogram()


mjs_plot(rbeta(10000, 2, 5)) %>%
  mjs_labs(x_label="rbeta(10000, 2, 3)") %>%
  mjs_histogram(bins=100) %>% 
  mjs_axis_y(extended_ticks=TRUE)

bimod <- c(rnorm(1000, 0, 1), rnorm(1000, 3, 1))
mjs_plot(bimod) %>% mjs_histogram() 
mjs_plot(bimod) %>% mjs_histogram(bins=30) 

bimod %>% mjs_hist(30)

library(shiny)
library(metricsgraphics)

ui = shinyUI(fluidPage(
  h3("MetricsGraphics Example", style="text-align:center"),
  metricsgraphicsOutput('mjs1'),
  br(),
  metricsgraphicsOutput('mjs2')
))

server = function(input, output) {

  mtcars %>%
    mjs_plot(x=wt, y=mpg, width=400, height=300) %>%
    mjs_point(color_accessor=carb, size_accessor=carb) %>%
    mjs_labs(x="Weight of Car", y="Miles per Gallon") -> m1

  set.seed(1492)
  stocks <- data.frame(
    time = as.Date('2009-01-01') + 0:9,
    X = rnorm(10, 0, 1),
    Y = rnorm(10, 0, 2),
    Z = rnorm(10, 0, 4))

  stocks %>%
    mjs_plot(x=time, y=X) %>%
    mjs_line() %>%
    mjs_add_line(Y) %>%
    mjs_add_line(Z) %>%
    mjs_axis_x(xax_format="date") %>%
    mjs_add_legend(legend=c("X", "Y", "Z")) -> m2

  output$mjs1 <- renderMetricsgraphics(m1)

  output$mjs2 <- renderMetricsgraphics(m2)

}

shinyApp(ui = ui, server = server)

There's another example provided by https://github.com/DocOfi which can be viewed at http://rpubs.com/DocOfi/352947. The Rmd file that created the example can be found at https://github.com/DocOfi/datasciencecoursera/tree/master/Exploratory_Data_Analysis/MetricsGraphics

Code of Conduct

Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.


On CRAN: http://cran.r-project.org/web/packages/metricsgraphics/index.html

Vignette: http://cran.r-project.org/web/packages/metricsgraphics/vignettes/introductiontometricsgraphics.html

You can see [core examples] (http://rpubs.com/hrbrmstr/53741) and fairly extended grid example on RPubs.


Download Details:

Author: hrbrmstr
Source Code: https://github.com/hrbrmstr/metricsgraphics 
License: View license

#r #graphic #d3 #html 

Htmlwidget interface to The MetricsGraphics.js D3 Chart Library

Automated integrated Regression Tests for Graphics Libraries

VisualRegressionTests.jl

Easy regression testing for visual packages. Automated tests compare similarity between a newly generated image and a reference image using the Images package. While in interactive mode, the tests can optionally pop up a Gtk GUI window showing a side-by-side comparison of the test and reference image, and then optionally overwrite the reference image with the test image. This allows for straightforward regression testing of image data, even when the "correct" images change over time.

Usage:

Two macros are provided that can be used to perform visual regression. The first macro is for general visual objects:

@visualtest testfun refimg popup tol

where:

  • testfun is a function that takes a filename as input, produces a visual, and saves it to disk:
function testfun(fname)
  visual = produce() # produce some visual object
  save(fname, visual) # save visual to file using filename
end

refimg is the filename where to save the reference image for regression testing

popup tells whether or not a Gtk popup window should be shown in case of mismatch (default to true)

tol the tolerance of the comparison (default to 0.02)

The second macro is for plots generated with Plots.jl:

@plottest plotfun refimg popup tol

where the only difference is in the plotfun function. In this case, the function should take no argument, and produce a plot, without saving it. The macro will take care of saving the image as a PNG in the disk. Alternatively, the plotfun argument can be an entire sequence of commands (i.e. a function body):

@plottest begin
  plot([1.,2.,3.])
  plot!([3.,2.,1.])
  # ...
end "foo.png"

Example GUI popup:

popup

Download Details:

Author: JuliaPlots
Source Code: https://github.com/JuliaPlots/VisualRegressionTests.jl 
License: View license

#julia #tests #graphic 

Automated integrated Regression Tests for Graphics Libraries
Nat  Grady

Nat Grady

1659965645

Cranvas: interactive Graphics In R using Qt Interfaces

Cranvas

Interactive graphics in R using Qt interfaces, a descendant of ggobi software. 

Installation

This wiki page is no longer maintained. Please see http://cranvas.org instead.

Common Features

Specific Functions

For Developers

Misc

Download Details:

Author: ggobi
Source Code: https://github.com/ggobi/cranvas 

#r #interactive #graphic 

Cranvas: interactive Graphics In R using Qt Interfaces
Nat  Grady

Nat Grady

1659799080

Showtext: Using Fonts More Easily in R Graphs

showtext

What's this package all about?

showtext makes it easy to use various types of fonts (TrueType, OpenType, Type 1, web fonts, etc.) in R plots. The motivation to develop this package is that using non-standard fonts in R plots (especially for PDF device) is not straightforward, for example, when creating PDF with Chinese characters. This is because most of the standard fonts used by pdf() do not contain Chinese character glyphs, and users could hardly use system fonts in R.

The extrafont package developed by Winston Chang is one nice solution to this problem, which mainly focuses on using TrueType fonts (.ttf) in PDF graphics device. Now showtext is able to support more font formats and more graphics devices, and avoids using external software such as Ghostscript.

A quick example

library(showtext)
## Loading Google fonts (https://fonts.google.com/)
font_add_google("Gochi Hand", "gochi")
font_add_google("Schoolbell", "bell")
font_add_google("Covered By Your Grace", "grace")
font_add_google("Rock Salt", "rock")

## Automatically use showtext to render text for future devices
showtext_auto()

## Tell showtext the resolution of the device,
## only needed for bitmap graphics. Default is 96
showtext_opts(dpi = 96)

set.seed(123)
x = rnorm(10)
y = 1 + x + rnorm(10, sd = 0.2)
y[1] = 5
mod = lm(y ~ x)

op = par(cex.lab = 2, cex.axis = 1.5, cex.main = 2)
plot(x, y, pch = 16, col = "steelblue",
     xlab = "X variable", ylab = "Y variable", family = "gochi")
grid()
title("Draw Plots Before You Fit A Regression", family = "bell")
text(-0.5, 4.5, "This is the outlier", cex = 2, col = "steelblue",
     family = "grace")
abline(coef(mod))
abline(1, 1, col = "red")
par(family = "rock")
text(1, 1, expression(paste("True model: ", y == x + 1)),
     cex = 1.5, col = "red", srt = 20)
text(0, 2, expression(paste("OLS: ", hat(y) == 0.79 * x + 1.49)),
     cex = 1.5, srt = 15)
legend("topright", legend = c("Truth", "OLS"), col = c("red", "black"), lty = 1)

par(op)

quick_example

In this example we first load some fonts that are available online through Google Fonts, and then tell R to render text using showtext by calling the showtext_auto() function. All the remaining part is exactly the same as the usual plotting commands.

This example should work on most graphics devices, including pdf(), png(), postscript(), and on-screen devices such as windows() on Windows and x11() on Linux.

How showtext works

Let me first explain a little bit how pdf() works.

To my best knowledge, the default PDF device in R does not "draw" the text, but actually "describes" the text in the PDF file. That is to say, instead of drawing lines and curves of the actual glyph, it only embeds information about the text, for example what characters it has, which font it uses, etc.

However, the text with declared font may be displayed differently in different OS, which means that the appearance of graph created by pdf() is system dependent. If you unfortunately do not have the declared font in your system, you may not be able to see the text correctly at all.

In comparison, showtext package tries to solve this problem by converting text into color-filled polygonal outlines (for vector graphics) or raster images (for bitmap and on-screen graphics), thus having the same appearance under all platforms. People who view this graph do not need to install the font that creates the graph. It provides convenience to both graph makers and graph viewers.

More importantly, showtext can use system font files, so you can show text in the graph with your favourite font face, as long as it is supported by FreeType. See section Loading fonts below.

The usage

To create a graph using a specified font, you simply do the following:

  • (*) Load the font to be used by showtext
  • Open the graphics device
  • (*) Claim that you want to use showtext to draw the text
  • Plot
  • Close the device

Only the steps marked with (*) are new. If you want to use showtext globally, you can call the function showtext_auto() once, and then all the devices after that will automatically use showtext to render text, as the example in the beginning shows.

If you want to have finer control on which part of the code should use showtext, functions showtext_begin() and showtext_end() will help. Only plotting functions enclosed by this pair of calls will use showtext, and others not. For example, to change the title font only, we can do:

library(showtext)
font_add_google("Schoolbell", "bell")

## By default the automatic call of showtext is disabled
## You can manually turn it off using the line below
## showtext_auto(enable = FALSE)

## To use showtext_begin() and showtext_end() you need to
## explicitly open a graphics device
png("demo.png", 700, 600, res = 96)
set.seed(123)
x = rnorm(10)
y = 1 + x + rnorm(10, sd = 0.2)
y[1] = 5
mod = lm(y ~ x)

op = par(cex.lab = 1.5, cex.axis = 1.5, cex.main = 2)
plot(x, y, pch = 16, col = "steelblue",
     xlab = "X variable", ylab = "Y variable")
grid()

## Use showtext only for this part
showtext_begin()
title("Draw Plots Before You Fit A Regression", family = "bell")
showtext_end()

text(-0.5, 4.5, "This is the outlier", cex = 2, col = "steelblue")
abline(coef(mod))
abline(1, 1, col = "red")
text(1, 1, expression(paste("True model: ", y == x + 1)),
     cex = 1.5, col = "red", srt = 20)
text(0, 2, expression(paste("OLS: ", hat(y) == 0.79 * x + 1.49)),
     cex = 1.5, srt = 15)
legend("topright", legend = c("Truth", "OLS"), col = c("red", "black"), lty = 1)

par(op)
dev.off()

demo-2

Loading fonts

Loading font is actually done by package sysfonts.

The easy way to load font into showtext is by calling font_add(family, regular), where family is the name that you assign to that font (so that later you can call par(family = ...) to use this font in plotting), and regular is the path to the font file. That is to say, only knowing the "font name" is not enough, since they are usually system dependent. On the contrary, font file is the entity that actually provides the character glyphs.

Usually the font files are located in some "standard" directories in the system (for example on Windows it is typically C:\Windows\Fonts). You can use font_paths() to check the current search path or add a new one, and use font_files() to list available font files in the search path. font_files() also lists some other useful information, for example the family name that you usually use to specify a font, and the font face for different variants. Below is an example to show the results on my machine:

head(font_files())

##                     path                           file               family
## 1 ***/.local/share/fonts            Flavors-Regular.ttf              Flavors
## 2 ***/.local/share/fonts FrederickatheGreat-Regular.ttf Fredericka the Great
## 3 ***/.local/share/fonts           GandhiSerif-Bold.otf         Gandhi Serif
## 4 ***/.local/share/fonts     GandhiSerif-BoldItalic.otf         Gandhi Serif
## 5 ***/.local/share/fonts         GandhiSerif-Italic.otf         Gandhi Serif
## 6 ***/.local/share/fonts        GandhiSerif-Regular.otf         Gandhi Serif
##
##          face       version                    ps_name
## 1     Regular Version 1.001            Flavors-Regular
## 2     Regular Version 1.001 FrederickatheGreat-Regular
## 3        Bold Version 1.001           GandhiSerif-Bold
## 4 Bold Italic Version 1.001     GandhiSerif-BoldItalic
## 5      Italic Version 1.001         GandhiSerif-Italic
## 6     Regular Version 1.001        GandhiSerif-Regular

And the code below demonstrates how to load and use system fonts on Windows:

library(showtext)
## Add fonts that are available on Windows
font_add("heiti", "simhei.ttf")
font_add("constan", "constan.ttf", italic = "constani.ttf")

library(ggplot2)
p = ggplot(NULL, aes(x = 1, y = 1)) + ylim(0.8, 1.2) +
    theme(axis.title = element_blank(), axis.ticks = element_blank(),
          axis.text = element_blank()) +
    annotate("text", 1, 1.1, family = "heiti", size = 15,
             label = "\u4F60\u597D\uFF0C\u4E16\u754C") +
    annotate("text", 1, 0.9, label = 'Chinese for "Hello, world!"',
             family = "constan", fontface = "italic", size = 12)

## Automatically use showtext for new devices
showtext_auto()

## On-screen device
x11()
print(p)
dev.off()

## PDF device
pdf("showtext-example-3.pdf", 7, 4)
print(p)
dev.off()

## PNG device
ggsave("showtext-example-4.png", width = 7, height = 4, dpi = 96)

## Turn off if no longer needed
showtext_auto(FALSE)

example1

For other OS, you may not have the simhei.ttf font file, but there is no difficulty in using something else. At present font_add() supports TrueType fonts(*.ttf/*.ttc) and OpenType fonts(*.otf), and adding new font type is trivial as long as FreeType supports it.

Also, there are many free fonts available and accessible on the web, for instance the Google Fonts project (https://fonts.google.com/). sysfonts provides an interface to automatically download and register those fonts through the function font_add_google(), as the example below shows.

library(showtext)
font_add_google("Lobster", "lobster")

showtext_auto()

plot(1, pch = 16, cex = 3)
text(1, 1.1, "A fancy dot", family = "lobster", col = "steelblue", cex = 3)

example2

CJK fonts

showtext includes an open source CJK (Chinese, Japanese, and Korean) font WenQuanYi Micro Hei. If you just want to show CJK text in your graph, simply specify the wqy-microhei family name in plotting functions.

Another option is to install the Source Han Sans/Serif fonts locally using the following code:

library(showtext)
font_install(source_han_serif())
font_families()
## [1] "sans"                "serif"               "mono"                "wqy-microhei"       
## [5] "source-han-serif-cn"

See ?font_install and ?source_han for more details.

source han

The internals of showtext

Every graphics device in R implements some functions to draw specific graphical elements, e.g., path() and polygon() to draw polygons, raster() to display bitmap images, text() or textUTF8() to show text, etc. What showtext does is to override their own text rendering functions and replace them by hooks provided in showtext that will further call the device's path() or raster() functions to draw the character glyphs.

This action is done only when you call showtext_begin() and won't modify the graphics device if you call showtext_end() to restore the original device functions back.

Compatibility with RStudio

Starting from version 0.9, showtext can work well with the RStudio graphics device (RStudioGD). Simply call showtext_auto() in the RStudio session and then the plots will be displayed correctly.

Original article source at: https://github.com/yixuan/showtext 

#r #font #graphic 

Showtext: Using Fonts More Easily in R Graphs
Nat  Grady

Nat Grady

1659791700

Extrafont: Tools for using Fonts in R Graphics

extrafont

The extrafont package makes it easier to use fonts other than the basic PostScript fonts that R uses. Fonts that are imported into extrafont can be used with PDF or PostScript output files. On Windows, extrafont will also make system fonts available for bitmap output.

There are two hurdles for using fonts in PDF (or Postscript) output files:

  • Making R aware of the font and the dimensions of the characters.
  • Embedding the fonts in the PDF file so that the PDF can be displayed properly on a device that doesn't have the font. This is usually needed if you want to print the PDF file or share it with others.

The extrafont package makes both of these things easier.

Presently it allows the use of TrueType fonts with R, and installation of special font packages. Support for other kinds of fonts will be added in the future. It has been tested on Mac OS X 10.7 and Ubuntu Linux 12.04 and Windows XP.

The instructions below are written for PDF files, although the information also applies to PostScript files.

If you want to use the TeX Computer Modern fonts in PDF files, also see the fontcm package.

Using extrafont

Requirements

You must have Ghostscript installed on your system for embedding fonts into PDF files.

Extrafont requires the extrafontdb package to be installed. extrafontdb contains the font database, while this package contains the code to install fonts and register them in the database.

It also requires the Rttf2pt1 package to be installed. Rttf2pt1 contains the ttf2pt1 program which is used to read and manipulate TrueType fonts. It is in a separate package for licensing reasons.

Install extrafont from CRAN will automatically install extrafontdb and Rttf2pt1:

install.packages('extrafont')
library(extrafont)

To use extrafont in making graphs, you'll need to do the following:

  • Import fonts into the extrafont database. (Needs to be done once)
  • Register the fonts from the extrafont database with R's PDF (or PostScript) output device. (Needs to be done once per R session)
  • Create the graphics that use the fonts.
  • Embed the fonts into the PDF file. (Needs to be done for each file)

Import fonts into the extrafont database

First, import the fonts installed on the system. (This only works with TrueType fonts right now.)

font_import()
# This tries to autodetect the directory containing the TrueType fonts.
# If it fails on your system, please let me know.

This does the following:

  • Finds the fonts on your system.
  • Extracts the FontName (like ArialNarrow-BoldItalic).
  • Extracts/converts a PostScript .afm file for each font. This file contains the font metrics, which are the rectangular dimensions of each character that are needed for placement of the characters. These are not the glyphs, which the curves defining the visual shape of each character. The glyphs are only in the .ttf file.
  • Scan all the resulting .afm files, and save a table with information about them. This table will be used when making plots with R.
  • Creates a file Fontmap, which contains the mapping from FontName to the .ttf file. This is required by Ghostscript for embedding fonts.

You can view the resulting table of font information with:

# Vector of font family names
fonts()

# Show entire table
fonttable()

If you install new fonts on your computer, you'll have to run font_import() again.

Register the fonts with the PDF output device

The next step is to register the fonts in the afm table with R's PDF (or PostScript) output device. This is needed to create PDF files with the fonts. As of extrafont version 0.13, this must be run only in the first session when you import your fonts. In sessions started after the fonts have been imported, simply loading the package with library(extrafont) this step isn't necessary, since it will automatically register the fonts with R.

# Only necessary in session where you ran font_import()
loadfonts()
# For PostScript output, use loadfonts(device="postscript")
# Suppress output with loadfonts(quiet=TRUE)

Create figures with the fonts

Here's an example of PDFs made with base graphics and with ggplot2. These examples use the font Impact, which should be available on Windows and Mac. (Use fonts() to see what fonts are available on your system)

pdf("font_plot.pdf", family="Impact", width=4, height=4)
plot(mtcars$mpg, mtcars$wt, 
     main = "Fuel Efficiency of 32 Cars",
     xlab = "Weight (x1000 lb)",
     ylab = "Miles per Gallon")
dev.off()


library(ggplot2)
p <- ggplot(mtcars, aes(x=wt, y=mpg)) + geom_point() +
  ggtitle("Fuel Efficiency of 32 Cars") +
  xlab("Weight (x1000 lb)") + ylab("Miles per Gallon") +
  theme(text=element_text(size=16, family="Impact"))

ggsave("font_ggplot.pdf", plot=p,  width=4, height=4)

The first time you use a font, it may throw some warnings about unknown characters. This should be harmless, but if it causes any problems, please report them.

Embed the fonts

After you create a PDF output file, you should embed the fonts into the file. There are 14 PostScript base fonts never need to be embedded, because they are included with every PDF/PostScript renderer. All other fonts should be embedded into the PDF files.

First, if you are running Windows, you may need to tell it where the Ghostscript program is, for embedding fonts. (See Windows installation notes below.)

# Needed only on Windows - run once per R session
# Adjust the path to match your installation of Ghostscript
Sys.setenv(R_GSCMD = "C:/Program Files/gs/gs9.05/bin/gswin32c.exe")

As the name suggests, embed_fonts() will embed the fonts:

embed_fonts("font_plot.pdf", outfile="font_plot_embed.pdf")
embed_fonts("font_ggplot.pdf", outfile="font_ggplot_embed.pdf")
# If outfile is not specified, it will overwrite the original file

To check if the fonts have been properly embedded, open each of the PDF files with Adobe Reader, and go to File->Properties->Fonts. If a font is embedded, it will say "Embedded Subset" by the font's name; otherwise it will say nothing next to the name.

With Adobe Reader, if a font is not embedded, it will be substituted by another font. This provides a way to see what your PDF will look like on printer or computer that doesn't have the font installed. Other PDF viewers may behave differently. For example, the Preview application on Mac OS X will automatically use system fonts to display non-embedded fonts -- this makes it impossible to tell whether the font is embedded in the PDF.

On Linux you can also use evince (the default PDF viewer) to view embedded fonts. Open the file and go to File->Properties->Fonts. If a font is embedded, it will say "Embedded subset"; otherwise it will say "Not embedded".

If you are putting multiple PDF figures into a single document, it is more space-efficient to not embed fonts in each figure, but instead embed the font in the final PDF document.

Windows bitmap output

extrafont also makes it easier to use fonts in Windows for on-screen or bitmap output.

# Register fonts for Windows bitmap output
loadfonts(device="win")

ggplot(mtcars, aes(x=wt, y=mpg)) + geom_point() +
  ggtitle("Title text goes here") +
  theme(plot.title = element_text(size = 16, family="Georgia", face="italic"))

ggsave("fonttest-win.png")

Since the output is a bitmap file, there's no need to embed the fonts.

Font packages

Extrafont supports font packages, which contain fonts that are packaged in a particular way so that they can be imported into extrafont. These fonts are installed as R packages; they are not installed for the computer operating system. Fonts that are installed this way will be available only for PDF or PostScript output. They will not be available for on-screen or bitmap output, which requires that the font be installed for operating system, not just with R and extrafont.

Presently extrafont supports only font packages with PostScript Type 1 fonts.

See the fontcm package containing Computer Modern fonts for an example.


Installation notes

Rttf2pt1

The source code for the utility program ttf2pt1 is in the package Rttf2pt1. CRAN has pre-compiled Windows and Mac OS X binaries. For other platforms, and when installing from source, it will be compiled on installation, so you need a build environment on your system.

Windows installation notes

In Windows, you need to make sure that Ghostscript is installed.

In each R session where you embed fonts, you will need to tell R where Ghostscript is installed. For example, when Ghostscript 9.05 is installed to the default location, running this command will do it (adjust the path for your installation):

Sys.setenv(R_GSCMD="C:/Program Files/gs/gs9.05/bin/gswin32c.exe")

Resetting the font database

To reset the extrafont database, reinstall the extrafontdb package:

install.packages("extrafontdb")

Original article source at: https://github.com/wch/extrafont

#r #graphic #tool 

Extrafont: Tools for using Fonts in R Graphics
Reid  Rohan

Reid Rohan

1656956820

D3-snippets: An Atom Package with D3v5 Snippets

d3-snippets

An Atom package with D3v5 snippets. Accelerate your graphics!

Contributions are appreciated, if you miss a snippet feel free to create an issue or open a pull request.

d3-snippets in action

Install

You can install it inside Atom (just search for d3-snippets) or via command line:

$ apm install d3-snippets

Reference

# app <>

Append something.

.append('${1:}')

# area <>

Area generator.

const area = d3.area()
    .x(d => x(d.${1:}))
    .y1(d => y(d.${2:}))
    .y0(y(0));

# attr <>

Set attributes.

.attr('${1:}', '${2:}')

# axisb <>

Bottom-oriented axis.

d3.axisBottom(${1:x});

# axisl <>

Left-oriented axis.

d3.axisLeft(${1:y});

# axisr <>

Right-oriented axis.

d3.axisRight(${1:y});

# axist <>

Top-oriented axis.

d3.axisTop(${1:x});

# axis <>

Create a SVG axis.

d3.axis()
    .scale(${1:})
    .ticks(${2:})
    .orient('${3:}')

# createblock <>

Scaffold a block.

<!DOCTYPE html>
<style>
</style>
<body>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
${1:}
</script>

# circle <>

Create a SVG circle.

.enter()
    .append('circle')
    .attr('cx', ${1:})
    .attr('cy', ${2:})
    .attr('r', ${3:})
    .style('fill', '${4:#111}');

# class <>

Set the element class.

.attr('class', '${1:}')

# csv <>

Load a CSV file.

d3.csv('${1:}').then(data => {
  ${2:console.log(data);}
});

# curve <>

Curve shorthand.

.curve(d3.${1:curveCatmullRom}.alpha(0.5));

# fdi <>

Return the data and the index.

(d, i) => ${1:}

# fd <>

Return the data.

d => ${1:}

# dom <>

Set the scale domain.

.domain([${1:}, ${2:}])

# dur <>

Set the transition duration.

.duration(${1:})

# ellipse <>

Create a SVG ellipse.

.enter().append('ellipse')
    .attr('cx', ${1:})
    .attr('cy', ${2:})
    .attr('rx', ${3:})
    .attr('ry', ${4:})
    .style('fill', '${5:#111}');

# ent <>

Enter the data.

.enter()

# extent <>

Set the dataset extent.

d3.extent(${1:data}, d => d.${2:value});

# fill-opacity <>

Set the element fill opacity.

.attr('fill-opacity', ${1:0.5})

# fill <>

Set the element fill.

.attr('fill', '${1:none}')

# fn <>

Blank anonymous function.

() => ${1:}

# geomap <>

Create the projection and path for a map.

const projection = d3.${1:geoMercator}()
    .fitSize([${2:width}, ${3:height}], ${4:land});

const path = d3.geoPath()
    .projection(projection);
${7:}

# g <>

Create a SVG group.

svg
    .append('g')
    .attr('class', '${1:}')

# join <>

Start with a data join.

d3.selectAll('${1:}')
    .data(${2:})

# json <>

Load a JSON file.

d3.json('${1:}').then(data => {
  ${2:console.log(data);}
});

# lineg <>

Line generator.

const line = d3.line()
  .x(d => x(d.${1:}))
  .y(d => y(d.${2:}));

# line <>

Create a SVG Line.

.enter().append('line')
    .attr('x1', ${1:})
    .attr('y1', ${2:})
    .attr('x2', ${3:})
    .attr('y2', ${4:})
    .style('stroke', '${5:#111}');

# locale <>

Set a default locale.

const ${1:en_US} = {
    'decimal': '.',
    'thousands': ',',
    'grouping': [3],
    'currency': ['$', ''],
    'dateTime': '%a %b %e %X %Y',
    'date': '%m/%d/%Y',
    'time': '%H:%M:%S',
    'periods': ['AM', 'PM'],
    'days': ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
    'shortDays': ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
    'months': ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
    'shortMonths': ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
}

formatDefaultLocale(${2:en_US});
timeFormatDefaultLocale(${3:en_US});

# margin <>

Append a SVG with the margin convention.

const margin = {top: ${1:20}, right: ${2:10}, bottom: ${3:20}, left: ${4:10}},
    width = ${5:960} - margin.left - margin.right,
    height = ${6:500} - margin.top - margin.bottom;

const svg = d3.select('${7:body}').append('svg')
    .attr('width', width + margin.left + margin.right)
    .attr('height', height + margin.top + margin.bottom)
  .append('g')
    .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

${8:}

# max <>

Get the maximum value.

d3.max(${1:data}, d => d.${2:value});

# min <>

Get the minimum value.

d3.min(${1:data}, d => d.${2:value});

# nest <>

Nest a dataset.

const nest = d3.nest()
    .key(d => d.${1:})
    .entries(${2:});

# on <>

Listen for events on the selection.

.on('${1:}', ${2:})

# queue-promise <>

Load multiple files using a Promise.

Promise.all([
    d3.${1:json}('${2:}'),
    d3.${3:json}('${4:}')
  ]).then([${5:file1}, ${6:file2}] => {
    console.log(${7:file1}, ${8:file1});
  })

# queue <>

Create a queue to load multiple files.

d3.queue()
    .defer(${1:d3.json}, '${2:}')
    .defer(${3:d3.json}, '${4:}')
    .await(function(error, ${5:file1}, ${6:file2}) {
        console.log(${7:file1}, ${8:file1});
    });

# r <>

Set the element radius.

.attr('r', ${1:5})

# ran <>

Set the scale range.

.range([${1:}, ${2:}])

# rect <>

Create a SVG rect.

.enter().append('rect')
    .attr('x', ${1:})
    .attr('y', ${2:})
    .attr('width', ${3:width})
    .attr('height', ${4:height})
    .attr('rx', ${5:0})
    .attr('ry', ${6:0})
    .style('fill', '${7:#111}');

# seq <>

Create a sequential scale.

d3.scaleSequential(d3.${1:interpolateViridis})
    .domain([${2:}])

# scale <>

Create a sample scale.

d3.${1:scaleLinear}()
    .domain([${2:}, ${3:}])
    .range([${4:}, ${5:}]);

# sel <>

Select an element.

d3.select('${1:}')

# sela <>

Select all the elements.

d3.selectAll('${1:}')

# sort <>

Sort a dataset.

${1:data}.sort((a, b) => ${2:a}.${3:value} - ${4:b}.${5:value});

# stroke-opacity <>

Set the element stroke opacity.

.attr('stroke-opacity', ${1:0.5})

# stroke-width <>

Set the element stroke width.

.attr('stroke-width', ${1:1})

# stroke <>

Set the element stroke.

.attr('stroke', '${1:black}')

# style <>

Set the element style.

.style('${1:}', '${2:}')

# anchor <>

Set the text anchor.

.attr('text-anchor', '${1:middle}')

# text <>

Set the element text.

.text('${1:}')

# tickSize <>

Set the tick size.

.tickSize(${1:})

# tickValues <>

Set the tick values.

.tickValues(['${1:}'])

# translate <>

Translate the element.

.attr('transform', `translate(${${1:0}},${${2:0}})`)

# voronoi <>

Create a Voronoi diagram.

const voronoi = d3.voronoi()
    .x(d => x(d.${1:}))
    .y(d => y(d.${2:}))
    .extent([[0, 0], [${3:width}, ${4:height}]]);

const voronoiGroup = svg.append('g')
    .attr('class', 'voronoi');

voronoiGroup.selectAll('path')
    .data(voronoi.polygons(${5:data}))
    .enter().append('path')
    .attr('d', d => d ? 'M' + d.join('L') + 'Z' : null)

# x <>

Set the x position.

.attr('x', ${1:})

# y <>

Set the y position.

.attr('y', ${1:})

Hacking

$ cd ~/.atom/packages
$ git clone git@github.com:martgnz/d3-snippets.git
$ cd d3-snippets
$ apm install
$ apm link

Credit

Most of the inspiration comes from fabriotav's and shancarter's Sublime Text packages.

Author: Martgnz
Source Code: https://github.com/martgnz/d3-snippets 
License: MIT license

#javascript #d3 #graphic 

D3-snippets: An Atom Package with D3v5 Snippets

Dragit: A toolkit To Enable The Manipulation Of Data Graphics

dragit.js

dragit is an extension to the D3.js library to enable the direct manipulation of SVG data visualization. It is designed to be seamlessly included in an existing D3 visualization. It is also designed to be highly customizable and extensible.

Getting Started

Code Organization

One of the library design goal to be included quasi-seamlessly in a current data visualization, i.e. without much change. To use it, insert the following snippets in the header of your code, right after D3:

<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="dragit.js" charset="utf-8"></script>

You are, however, very likely to structure the chart as follows to make sure dragit is informed on the existing temporal data and the current state of the visualization. Two functions (names don't matter) are to be created:

  • init() which is called only once during startup
  • update() which is called once time has changed

Those two functions will make sure the library's internal state is always up to date, regardless how you update the data visualization (using regular slider or direct manipulation).

Core Concepts

Here are a few concepts that are important to grasp:

  • Object (of Interest): the graphical marks (SVG node, div, ..) that can be dragged and will indirectly update the visualization.
  • Focus: the visual element that is being dragged (can be a simplified simplified such as into a point or shadow).
  • Trajectory: the visual path along which the Object of Interest can be dragged. It is represented as a line.
  • Data points: series of points the focus can reach on the trajectory.

Here are the names using for the various objects:

dragit.data

  • dragit.data: is a time-cube defined where each row are data points and columns time steps.

You may want to generate a random time cube (of nb_data_points x nb_time_steps). The x and y values should reflect the exact position of the data poin on screen. Example as below:

var timecube = d3.range(nb_data_points).map(function(d, i) {
    return d3.range(nb_time_steps).map(function(e, j) { 
        return {x: i, y: Math.random(), t: j};
    });
})

Example (rows are data, columns are time steps):

[ d0 [ t0 ] [ t1 ] ... [ tm ] ]
[ d1 [ t0 ] [ t1 ] ... [ tm ] ]
...
[ dn [ t0 ] [ t1 ] ... [ tm ] ]

Where di are dimensions, as ti are time points.

Some other Ways to generate a dataset that can be dragged:

  • Sampling a SVG shapes (see the spiral example)
  • Browsing paths of a tree data structure (see the radial tree example or the World Cup Brackets example)
  • Image / Video segmentation
  • Or more generally, use any other combinatorial space.

dragit.vars

A series of private variables for internal use. Can be set using publication functions, such as:

  • dragit.init(container) : set the DOM element containing all the trajectories (can be shared with the visualization)

dragit.time

  • dragit.time.current : the current time (default: 0)
  • dragit.time.min : the minimal time point (default: 0)
  • dragit.time.max : the maximal time point (default: 0)
  • dragit.time.step : increment (default: 1)
  • dragit.time.speed : for the playback (default)

Example:

dragit.time = {min: d3.min(data, function(d) { return parseInt(d[i]);}), 
                              max: d3.max(data, function(d) { return parseInt(d[i]);}), 
                              step: 1, 
                              current: 0
                            }

dragit.object

The object of interest, or the handle the user interacts with to start the interaction and thus start the time change.

  • dragit.object.activate activates dragging for the selected element. It creates the necessary mouse events (drag). Example: .call(dragit.object.activate).

dragit.object.dragging

  • absolute (default) does not translate the current chart
  • relative does translate the current chart to keep it centered around the current focus point

dragit.mouse

dragit.mouse.dragging

Below are the different drag-type strategies:

  • horizontal or vertical
  • flow flow dragging method. Usually well suited for background * motion.
  • free dragging with no constraints on the activated element, returns to its original position
  • curvilinear (not implemented)

dragit.mouse.scope

  • focus restricts closest point finding to the current trajectory, thus only change time.
  • selected expands the scope to all currently displayed trajectories, thus may change time and the current focus.

dragit.focus

  • Functions related to the focus that is being dragged

dragit.trajectory

Handles trajectories drawing.

  • dragit.trajectory.display(class) : displays the currently dragged element's trajectory
  • dragit.trajectory.displayUpdate : update the trajectory
  • dragit.trajectory.displayAll(class) : displays all trajectories
  • dragit.trajectory.toggle(class) : toggle the display of current trajectory
  • dragit.trajectory.toggleAll(class) : toggle the display of all trajectories
  • dragit.trajectory.remove(class) : removes the created trajectory
  • dragit.trajectory.removeAll(class) : removes all trajectories

dragit.evt

Events management mechanism to register and trigger functions.

  • dragit.evt.register(event, function, context) : register a function for a given event or an array of multiple event
  • dragit.evt.call(event) : trigger registered functions

dragit.statemachine

  • dragit.statemachine.current_state : the current state of the interaction (e.g mouseenter, dragstart)
  • dragit.statemachine.current_id : the id of the currently manipulated element
  • dragit.statemachine.setState(event) : sets the current state of the state machine
  • dragit.statemachine.getState() : gets the current state of the state machine

dragit.custom

Define the type of design for the focus point vars.custom_focus (default) and trajectories vars.custom_trajectory (default).

  • dragit.custom.line
  • dragit.custom.point

dragit.constraint

For each object, this allows to set some expected positions when multiple are available. This can be used in multiple scenarios:

To filter out a space and only make visible a specific trajectory

When multiple permutations are possible and we need to decide whiche one

dragit.constraint : array of constraints

dragit.utils

A collection of useful functions for path calculation and animation.

  • dragit.utils.animateTrajectory(path, star_time, duration) : animates the path from start_time and with a given duration
  • dragit.utils.closestPoint
  • dragit.utils.closestValue
  • dragit.utils.findYgivenX
  • dragit.utils.translateAlong(path, duration)
  • dragit.utils.slider(el, play_button) : automatically creates a slider to browse all the time points

Publication

Romain Vuillemot, Charles Perin. Investigating the Direct Manipulation of Ranking Tables. Proceedings of the 2015 Annual Conference on Human Factors in Computing Systems (CHI 2015), Apr 2015, Seoul, Korea. ACM.

Examples

More advanced ones:

  • Interactive soccer bracket (demo)
  • A Re-Recreation of Gapminder's Wealth of Nations (demo | source)

Coming soon

  • Ranking tables
  • Standard charts: Bar chart, pie chart, ..
  • Node link diagrams
  • Geo maps navigation

Author: Romsson
Source Code: https://github.com/romsson/dragit 
License: BSD-3-Clause license

#javascript #d3 #visualization #graphic 

Dragit: A toolkit To Enable The Manipulation Of Data Graphics

Swoopy-drag: Artisanal Label Placement for D3 Graphics

swoopyDrag

The annotation layer is the most important thing we do

- Amanda Cox -

swoopyDrag helps you hand place annotations on d3 graphics. It takes an array of objects representing annotations and turns them into lines and labels. Drag the text and control circles below to update the annotations array:

var annotations = [
  {
    "sepalWidth": 4.4,
    "sepalLength": 5.7,
    "path": "M-49,-61L-6,-8",
    "text": "Setosa",
    "textOffset": [
      -83,
      -65
    ]
  },
  {
    "sepalWidth": 3.8,
    "sepalLength": 7.7,
    "path": "M 15,114 A 128.931 128.931 0 1 1 -10,-32",
    "text": "Virginica",
    "textOffset": [
      -11,
      105
    ]
  },
  {
    "sepalWidth": 2.3,
    "sepalLength": 5,
    "path": "M-5,86C-47,82,-55,18,-9,4",
    "text": "Versicolor",
    "textOffset": [
      -2,
      92
    ]
  }
]

The x and y functions are called on each annotation to determine its position. In the annotations array here, the sepalWidth and sepalLength properties are the data values of point the annotation refers to. The functions passed to x and y look up these values and encode them as pixel position using the same scale set up to position the circles.

var swoopy = d3.swoopyDrag()
  .x(d => xScale(d.sepalWidth))
  .y(d => yScale(d.sepalLength))
  .draggable(true)
  .annotations(annotations)

Setting draggable to true adds control points to the path strings and enables label dragging - turn on while developing and off when you're ready to publish.

The shape of each annotation's line is determined by the path property, the text by the text property and the position of the text by the testOffset property. Currently only straight paths (paths of the form M 0,0 L 10,10), béziers (paths of the form M 0,0 C 10,10 10,15, 20,15) and circular arcs are supported—see my interactive path string tutorial for more details.

The annotations are added to the page just like d3.svg.axis - append a new group element and use call:

var swoopySel = svg.append('g').call(swoopy)

After posititioning the labels, open the dev tools, run copy(annotations) in the console and paste over the old annotations array in your text editor.

API Reference

d3.swoopyDrag()

Creates a new swoopyDrag.

swoopyDrag.x([function])

Function called on each annotation object to determine its x position.

swoopyDrag.y([function])

Function called on each annotation object to determine its y position.

swoopyDraw.draggable([boolean])

Boolean. Pass true while adjusting annotations to enable dragging and add control points to paths.

swoopyDrag.annotations([array])

Array of objects representing annotations. The path in each annotations will have its d attribute set to the path property. The text element will contain the text property and be translated by textOffset.

swoopyDrag.on('drag', [function])

Called as the labels or paths are dragged.

Demo/Documentation

Author: 1wheel
Source Code: https://github.com/1wheel/swoopy-drag 
License: MIT license

#javascript #d3 #graphic #3d 

Swoopy-drag: Artisanal Label Placement for D3 Graphics
Gordon  Taylor

Gordon Taylor

1656156660

D3panels: Library Of D3-based Graphic Panels, Written in CoffeeScript

d3panels: D3-based graphic panels

This is a set of D3-based graphic panels, to be combined into larger multi-panel charts. They were developed for the R/qtlcharts package.

Note that d3panels uses D3 version 7. (It should also work with version 6.)

There are other libraries with similar goals that are of more general use (e.g., C3.js); see this list of javascript chart libraries), but I wanted charts that were a bit less general and flexible, but rather more specific to my particular applications (and style).

Usage

All of the functions are called as d3panels.blah(). And for each chart, you first call the chart function with a set of options, like this:

mychart = d3panels.lodchart({height:600, width:800})

And then you call the function that's created with some selection and the data:

mychart(d3.select("div#chart"), mydata)

There are three exceptions to this: add_lodcurve, add_curves, and add_points. For these functions, you first need to call another function that creates a panel (for example, lodchart or chrpanelframe in the case of add_lodcurve, or panelframe in the case of add_curves or add_points). You then use the chart function created by that first call in place of a selection. For example:

myframe = d3panels.panelframe({xlim:[0,100],ylim:[0,100]})
myframe(d3.select("body"))

addpts = d3panels.add_points()
addpts(myframe, {x:[5,10,25,50,75,90], y:[8,12,50,30,80,90], group:[1,1,1,2,2,3]})

Links

You'll need to link to the d3panels.js and d3panels.css files (or to d3panels.min.js and d3panels.min.css):

<script type="text/javascript" src="https://rawgit.com/kbroman/d3panels/main/d3panels.js"></script>
<link rel=stylesheet type="text/css" href="https://rawgit.com/kbroman/d3panels/main/d3panels.css">

You'll also want to link to D3.js:

<script charset="utf-8" type="text/javascript" src="https://d3js.org/d3.v5.min.js"></script>

Build

To build the javascript (and CSS) files from the coffeescript source, you first need to install npm.

Then use npm to install yarn, coffeescript, uglify-js uglifycss, and babel-core

npm install -g yarn coffeescript uglifycss uglify-js babel-core

Then install the dependency, d3:

yarn install

Finally, run make to create the compiled javascript code.

make

For snapshots and live tests, see https://kbroman.org/d3panels.

Documentation

For documentation, see https://github.com/kbroman/d3panels/tree/main/doc.

Karl W Broman

Author: kbroman
Source Code: https://github.com/kbroman/d3panels 
License: MIT license

#javascript #d3 #graphic 

D3panels: Library Of D3-based Graphic Panels, Written in CoffeeScript
Recoding IO

Recoding IO

1654530028

Creating 2D Custom Graphics using SwiftUI Canvas View | SwiftUI 3.0

Canvas View introduces in SwiftUI 3. It helps to draw 2D Graphics inside SwiftUI View. Canvas View provides a native solution for SwiftUI developers it encapsulates context and size inside it.

Context or Graphics Context acts as a pencil for the developer it helps to carve out shapes inside the View. While the size provides the view bound of the Canvas View, size consists of the Width and Height of the frame.

In this video we gonna take a Look at how to draw 2D Graphics in SwiftUI using Canvas View.

https://www.youtube.com/watch?v=PDWqm3BKB1g

#swift  #ios #apple #xcode #macos #software-development #graphic 

Creating 2D Custom Graphics using SwiftUI Canvas View | SwiftUI 3.0
伊藤  直子

伊藤 直子

1654224420

Three.js、Blender、およびGreensockを使用してWebサイト用の素晴らしい3Dグラフィックスを作成する

WebGLまたはthree.jsで3Dグラフィックスを作成しようとしたことがあれば、おそらくすべてががらくたのように見えることを経験したことがあるでしょう。コードで複雑なジオメトリを作成することは困難であり、ライトは常にフラットで非現実的に見えます。

リアルなライトを作成するには、リアルタイムのレイトレーシングが必要です。これは、ほとんどのコンピューターハードウェアには重すぎます。そのため、代わりに、いくつかの高度な手法を使用して、リアルな照明を「偽造」し、照明条件を変更します。

しかし、最初に、3Dモデリングソフトウェアを使用してジオメトリを作成しましょう。無料でオープンソースのBlenderを使用することをお勧めします。ただし、任意の3Dモデリングソフトウェアを使用できます。

モデリング

Blenderで複雑な3Dモデルを作成する方法を学ぶには、ある程度の練習が必要です。通常、モデル化しようとしているものに最も近い形状を追加することから始めます。キーボードをモデリングする場合は、通常、開始点として立方体またはプレーンを使用し、カップの場合は、通常、円柱を使用します。

開始シェイプを追加すると、モデリングしているものにますます似るようにスカルプトし始めます。キーボードに関しては、おそらく高さを変更し、幅を伸ばすでしょう。

プロのヒント!すべてのエッジに少なくともいくつかの小さな斜角を追加します。これにより、後で照明を追加するときのリアリズムが向上します。

3Dモデルを作成すると、次の画像のようになります。このようなもののモデリングは、通常、コツをつかんでから数時間しかかかりません。

スクリーンショット—机のモデリング

材料

3Dジオメトリが完成したら、すべての異なるサーフェスにテクスチャとマテリアルを追加する必要があります。通常、光沢のある金属の表面は、ベーキングを使用してテクスチャを作成する場合は見栄えがよくないため、光沢の少ない素材を使用することをお勧めしますが、代わりに金属のテクスチャを使用します。それ以外は、創造性を発揮することも、木やプラスチックなどの実際の表面素材を模倣することもできます。

点灯

これで3Dジオメトリが作成され、Blenderなどを使用してマテリアルが適用されました。モデルをエクスポートしてWebGLで使用する前に、いくつかのライトを追加する必要があります。モデルに奥行きとリアリズムを追加するには、いくつかの光源を使用することをお勧めします。

原則として、少なくともバックライト、キーライト、およびフィルライトが必要です。キーライトとフィルライトは、2つの最も重要な光源です。それらは反対側から被写体を照らします。

キーライトはプライマリライトであり、フィルライトよりもわずかに強い必要があります。

プロのヒント!通常、冷たいライトは左側からシーンに入り、暖かいライトは右側から入ります。

照明を追加すると、シーンは次のようになります。

ベーキング

シーンが適切に照らされたので、材料を焼く必要があります。ベーキングは、UnrealやUnityなどのゲームエンジンがシーンにリアリズムを追加するために使用するトリックです。私たちの典型的なハードウェア(GPU)が、リアルタイムのレイトレーシングを処理するのに十分なほど強力ではなかったことを覚えていますか?この処理は事前に行うという考え方です。照明も含めるようにマテリアルを変更します。これが「ベーキング」と呼ばれるものです。注意点の1つは、照明をマテリアルに焼き付けると、静的になることです。つまり、3Dモデルを移動しても変化しません。

シャドウマップやライトマップなど、これを改善するためのいくつかの手法があります。ライトマップはサーフェスで反​​射された光のみを含むテクスチャであり、シャドウマップはシャドウを含むテクスチャです。これらを巧妙な方法で使用すると、リアルな照明のような錯覚を作り出すことができます。

とにかく、Blenderを使用して3Dモデルのテクスチャをベイクするには、最初に、適切にUVアンラップされていることを確認する必要があります。UVアンラッピングは、モデルに継ぎ目を追加して、平面上に展開できるようにするプロセスです。このようにして、2Dテクスチャを3Dモデルに投影できます。

単純な形状では、UVアンラッピングを自動的に実行できます。より複雑なタスクの場合、特に角の周りで奇妙なアーティファクトを回避するために、いくつかの継ぎ目を手動で追加する必要がある場合があります。

ラップを解除すると、テクスチャのベイク処理を開始できます。SimpleBakeなどのプラグインを使用することをお勧めします。それはベーキングを非常に簡単にします。高品質のテクスチャをベイク処理する場合、このプロセスには通常1時間以上かかります。

私のシーンでは、次の2つのテクスチャを焼きました。

焼きたてのジオメトリテクスチャ

周囲光を変更するためのライトマップ

Three.js

最後に、3Dモデルが完成し、モデルで使用するすべてのテクスチャをベイク処理しました。

次に、モデル(メッシュとも呼ばれます)とテクスチャをエクスポートする必要があります。通常、メッシュはGLTF / GLBとしてエクスポートし、テクスチャは個別のファイルとしてエクスポートします。エクスポートしたら、さまざまなローダーを使用して、Three.jsシーンにアセットをインポートできます。

import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import Three from 'threejs';

const textureLoader = new THREE.TextureLoader();
const modelLoader = new GLTFLoader();
modelLoader.setPath('./');

// load the assets
const setup = async () => {
  const model = await modelLoader.loadAsync('./desk.gltf');
  const texture = await textureLoader.loadAsync('./desk.png');
  const material = new THREE.MeshBasicMaterial({ map: texture });
  
  const scene = new THREE.Scene();

  // apply the texture to all objects in you model
  model.scene.traverse((_child) => {
    if(_child instanceof THREE.Mesh) {
      _child.material = material
    }
  });
  
  scene.add( model.scene );
}

これでシーンがレンダリングされ、いくつかのアニメーションを使用してシーンを生き生きとさせる時が来ました。Three.jsでシーンをアニメートするときは、を使用することが重要requestAnimationFrameです。

const animate = () => {
  requestAnimationFrame( animate );
  renderer.render( scene, camera );
}
animate();

これにより、画面が更新されるたびにレンダラーがシーンを描画するループが作成されます(通常の画面では、これは1秒間に60回を意味します)。これには、パフォーマンスと、ユーザーが別のブラウザタブに変更した場合にアニメーションが一時停止するという事実に関して、いくつかの利点があります。

Three.jsでシーンをアニメーション化するには、Greensockを使用することをお勧めします。Greensockは、Three.jsのオブジェクトとプロパティをアニメーション化するための強力なツールです。事前定義されたアニメーション曲線のセットから選択するか、独自の曲線を簡単に作成できます。


gsap.to(this.colors, {
  deskLightStrength: 0,
  pcLightStrength: 0,
  duration: 2,
  delay,
  onUpdate: () => {
    this.desk.changeColors({ pcLightStrength: this.colors.pcLightStrength, deskLightStrength: this.colors.deskLightStrength });
  },
});

最終的な考え

ご覧のように。three.jsを使用してWebGLで現実的な3Dモデルを学習し、作成することはそれほど難しくありません。ただし、ジオメトリをモデリングするためのツールを理解して操作する必要があり、シーンを適切に照明するための高度なテクニックを理解する必要があります。

最後に、Three.jsを使用してWebGLで3Dモデルとテクスチャを操作する方法、およびrequestAnimationFrameGreensockを使用してシーンをアニメーション化する方法を学習する必要があります。

読んでくれてありがとう!今後ともよろしくお願いいたします。

このストーリーは元々https://betterprogramming.pub/how-to-create-awesome-3d-graphics-for-your-website-using-three-js-blender-and-greensock-a834cb8a6f6eで公開されました

#threejs #greensock #blender #3d #graphic 

Three.js、Blender、およびGreensockを使用してWebサイト用の素晴らしい3Dグラフィックスを作成する