Giter Site home page Giter Site logo

mathatom's Introduction

MathAtom

This is a research project to investigate if code sharing between wpf-math and CSharpMath is possible, and if it is, then at what degree.

Eventually a new organisation and a new repository will be created, so please click the watch button so that you can follow along. Cheers!

mathatom's People

Contributors

charlesroddie avatar happypig375 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mathatom's Issues

Versioning

What would be the initial version?
A. 0.0.0 - start all over again
B. 0.3.0 - continue from CSharpMath
C. 0.7.0 - continue from wpf-math
D. 0.7.0 - release 0.3.0~0.6.0 of CSharpMath in a short period of time, then continue from both
E. 0.10.0 - bump the tens digit of minor release number
F. 1.0.0 - bump the major release number

I'm in favour of E.

Atom additions for editing

We could put extra cases inside the Atom type:

type Atom =
    ...
    /// Represents a blank, displayed as a square, where input is needed.
    | InputSquare
    // An Atom may have at most one Selected or Cursor inside.
    | Selected of Atom 
    | Cursor

type Input =
    | Del | Backspace
    | Left | Right | Up | Down
    | AddAtom of Atom
    | AddChar of char

let process (a:Atom) (i:Input) : Atom = ...

Alternatively we could make separate Atom from cursor position / selection:

type Atom =
    ...
    /// Represents a blank, displayed as a square, where input is needed.
    | InputSquare

type EditingState =
    // This requires identifying Atoms, so that atoms need to be distinct as objects:
    | Selected of Atom 
    | Cursorposition of ?

@Happypig375 thoughts appreciated as you work on the MathEditor port.

I think the first approach will be much cleaner. In the simplest implementation it may not be the most performant as l/r/u/d moves would lead to laying out everything again, but as long as it takes << 1/60 s to process a change we don't need to worry.

Initial code review

Can we move to a PR system as it exposes good commenting functionality?

In lieu of this some initial comments:

  • inline shouldn't be used everywhere. It should be used for parametrized types where the type system has problems or where specialization for performance is needed. Definitely doesn't make sense for non-generic functions.
  • (|Alphabet|NonAlphabet|) -> can simplify to let isAlphabetic c = . "alphabet" should include alpha and beta :).
  • [<CompiledName "Delimiters">]let delimiters -> let Delimiters
  • Can flatten type Options = { Delimiters: AliasMap<string, Delimiter> }, removing record.
  • AliasMap is not good. Pretending to be immutable doesn't work as an idea. Performance will be terrible. The API is too extensive and should be reduced to a minimum. This can be a lot simpler and does look like an F# experiment.

Re: value vs refernce types, I expect there will be some threshold below which value types will perform better (e.g. ValueOption<char*char>) and above which passing by reference will be more efficient. Not sure where that theshold is exactly though.

The Atom types in wpf-math and CSharpMath

Both libraries use C# and it was decided to stick with C#. The following is F# code because even if we don't compile it, it's good pseudocode.

The wpf-math Atom

type Atom =
    /// representing horizontal row of other atoms, separated by glue.
    | Row of Atom list
    /// single character in specific text style
    | Char of char * textStyle: string option
    /// character that does not depend on text style
    | FixedChar of c:char * fontId:int
    /// base atom with accent above it
    | Accented of Atom * Accent
    /// big delimeter (e.g. brackets)
    | BigDelimiter of delimiter:Atom * size:int // why is this called BigDelimiter not BigAtom?
    /// big operator with optional limits
    | BigOperator of baseAtom:Atom * upperLimit:Atom * lowerLimit:Atom // baseAtom must have type BigOperator
    /// base atom surrounded by delimeters
    | Fenced of baseAtom:Atom * leftDelimiter:Symbol * RightDelimiter:Symbol
    /// fraction, with or without separation line
    | Fraction of numerator:Atom * denominator:Atom * nAlign:XAlignment * dAlign:XAlignment
    /// other atom with horizontal rule above it
    | Overlined of baseAtom:Atom
    /// scripts to attach to other atom
    | Scripts of baseAtom:Atom * subscriptAtom: Atom option * superscriptAtom: Atom option
    /// whitespace
    | Space of width:float<mu> * height:float<mu>
    /// other atom that is not rendered
    | Phantom of baseAtom:Atom * useWidth:bool * useHeight:bool * useDepth:bool
    /// other atom with custom left and right types
    | Typed of atom:Atom * leftType:TexAtomType * rightType:TexAtomType
    /// single character that can be marked as text symbol
    | CharSymbol of isTextSymbol:bool
    /// symbol (non-alphanumeric character)
    | Symbol of name:char * TexAtomType * isDelimiter:bool
    /// other atom with delimeter and script atoms over or under it
    | OverUnderDelimiter of baseAtom:Atom * script:Atom * Symbol * kern:float<mu> * over:bool
    /// other atom that is underlined
    | Underlined of baseAtom:Atom
    /// other atom with atoms optionally over and under it
    | UnderOver of baseAtom:Atom * underOver:Atom * underOverSpace:float<mu>
    /// other atom vertically centered with respect to axis
    | VerticallyCentered of atom:Atom
    /// radical (nth-root) construction
    | Radical of baseAtom:Atom * degreeAtom:Atom
    /// Atom specifying graphical style.
    | Styled of RowAtom* background: Brush * foreground: Brush
    /// Dummy atom whose type can change or which can be replaced by a ligature.
    | Dummy of atom:Atom * isTextSymbol:bool

    /// gets the types of the left and rightmost children or the type of the atom itself if it childless
    member t.GetLR: TexAtomType * TexAtomType = ...
    member t.GetLeftType  = t.GetLR |> fst
    member t.GetRightType = t.GetLR |> snd
    member t.Type:TexAtomType = ...

In CSharp there is an Atom class and subclasses inherit from it.

These classes also contain code to generate a Box, which describes how to lay out and render it. I think this part can be moved into a separate layer and Atom classes just used to give a structural representation.

Could we try to start this project in F#?

The time has come.

I suggest we could try to write this project in F#. Here're my arguments.

Pros

  1. Obviously, @charlesroddie loves F# very much, we all know that :)
  2. I believe that working with immutable structures in F# is much more convenient than in C#, even with today's state of C# 7.3 (or even considering C# 8, F# is still better)
  3. In WPF-Math, we already had big troubles with mutability, and I had to invest a big part of my own time to fix all the issues and finally migrate to immutable structures, and I'm still unhappy with the result

Cons

  1. FSharp.Core is known to cause all kinds of troubles, e.g. rspeele/TaskBuilder.fs#15, and we cannot develop in F# without FSharp.Core, obviously
  2. I maybe will repeat my old arguments from some old WPF-Math thread, but F# creates a barrier for C# guys to contribute, I have no doubts about that
  3. F# API may be hard for C# guys to consume (but we can create C#-friendly API without too much troubles)
  4. F# DU-based API is hard to extend externally

Open questions

  1. When porting WPF-Math atoms to immutable structures (a very relevant experience), I found a couple of places where I think F# records would not help me: in some places, I want to update a part of a structure (e.g. replace SourceSpan or Style). Each of my structures has SourceSpan property, so I had to essentially implement the same stupid method WithSourceSpan(SourceSpan ss) => this.Clone(ss: ss) many times. We should think well about composability issues here (how should we compose the atoms, how could we implement typical workflow of CSharpMath or WPF-Math atom builder here)
  2. If we're going to model our stuff using convenient F# ways: DU and records โ€” then what are we going to do with external extendability? In OOP, external users may inherit from our Atoms and it would (hopefully) work well. But in F#, we have these options:
    • use object-oriented types in F# (we could still have small F# benefits here and there, but it won't be near as convenient as "native" F# way) and thus allow external users (WPF-Math and CSharpMath or anyone else) to extend our types as they want
    • use F#-native types (DU and records) and offer some functional way of extending things (I have no particular idea about what could we do)
    • forbid external extensions and require users to fit in our type system. From maintainer point of view, I found that thought very dangerous
    • some other options I don't see?

General thoughts

It we're going to follow the F# route, we'll need to establish an uncommonly (for F#-based projects, I mean) C#-friendly environment:

  • documentation for C# users (with live samples about how to use our DU/record types from C# code)
  • create sample projects in C# (and make sure to compile them on our CI so they won't become outdated)
  • maybe add some integration tests to compile C# samples with our F# code and various versions of .NET, .NET Core, Mono and, mainly, FSharp.Core to avoid any crazy issues*
  • Remember, end-user may end up with multiple FSharp.Core versions in their end-user software, and we should be ready for that; hopefully, modern .NET Core SDK / <PackageReference> tooling will save us from major headaches, but still: I insist that we need to check that regularly and systematically as part of our CI

Closing

@verybadcat, @alexreg I would appreciate your input very much, because I think you're much less biased towards F# than e.g. myself, @Happypig375, @charlesroddie or @gsomix.

Others: please also participate in the discussion, I appreciate your input as well :)

API shape + Type naming

I propose the following general API shape:

namespace To_Be_Determined_Library_Name.VisualAtom
    type VisualAtom = ...
namespace To_Be_Determined_Library_Name.MathAtom
   type MathAtom =
      | ...
   [<RequireQualifiedAccess>]
   module LaTeX =
      let ToAtom (s: string) : MathAtom =
         ...
      let FromAtom (a: MathAtom) : string =
         ...
   [<RequireQualifiedAccess>]
   module MathML =
      let ToAtom (s: string) : MathAtom =
         ...
      let FromAtom (a: MathAtom) : string =
         ...
   [<RequireQualifiedAccess>]
   module Visual =
      let FromAtom (a: MathAtom) : VisualAtom =
         ...
namespace To_Be_Determined_Library_Name.TextAtom
   type TextAtom =
      | ...
   [<RequireQualifiedAccess>]
   module LaTeX =
      let ToAtom (s: string) : TextAtom =
         ...
      let FromAtom (a: TextAtom) : string =
         ...
   [<RequireQualifiedAccess>]
   module Visual =
      let FromAtom (a: TextAtom) : VisualAtom =
         ...
namespace To_Be_Determined_Library_Name.VisualAtom
   type ICanvas =
      abstract member DrawLine(...) =
         ...
      ...
   [<RequireQualifiedAccess>]
   module ICanvas =
      let DrawOn (c: ICanvas) (v: VisualAtom) : unit =
         ...

image

How to manage styles and colors

Can we discuss the best way to describe styles (colors, italics, fonts, ...) in the Atom type?

Currently:

wpf-math CSharpMath Comment
Char of char * textStyle: string option
FixedChar of c:char * fontId:int
Styled of RowAtom* background: Brush * foreground: Brush Styled of Atom * Style

Color of Atom * colorString: string

Should we have a single Atom case Styled of Atom * Style to give style information?

type Style = System.Drawing.Color option * italic: bool option * bold: bool option * font: string option

Where inner styles take precedence over outer styles, and there is a default style which is the most outer style? Would this work?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.