Giter Site home page Giter Site logo

fontforge / libspiro Goto Github PK

View Code? Open in Web Editor NEW
101.0 39.0 25.0 1 MB

Spiro is the creation of Raph Levien. It simplifies the drawing of beautiful curves. (Migrated here from libspiro.sourceforge.net on 2013-04-20)

License: GNU General Public License v3.0

C 71.56% Java 16.09% Makefile 2.36% M4 8.60% Roff 1.39%

libspiro's Introduction

Spiro Coverity Scan Build Status

Introduction

Spiro is the creation of Raph Levien (ppedit 20070504, GPL2+). It simplifies the drawing of beautiful curves.

LibSpiro is an adaptation of Spiro formula and functions into a sharable library George Williams (libspiro 20071029, GPL2+).

Using Bézier splines an artist can easily draw curves with the same slope on either side of an on-curve point. Spiros, on the other hand, are based on clothoid splines which make it easy to maintain constant curvature as well as constant slope. Such curves will simply look nicer.

Raph Levien's spiro splines only use on-curve points and so are easier to use and more intuitive to the artist.

This library will take an array of spiro control points and convert them into a series of Bézier splines which can then be used in the myriad of ways the world has come to use Béziers.

Installation

Installing from Git master requires 2 preparatory steps:

First, you need to create the ./configure script if you do not have it yet

autoreconf -i  (or use 'autoreconf --install --force' for more modern setups)
automake --foreign -Wall

Second, you then use the usual steps to compile the library. Various operating systems and setups will need ./configure options set. The INSTALLATION file has detailed info for `configure' options. Example install steps for Linux, FreeBSD, Win32/64 are shown below:

Installing on Linux

./configure
make
make check
sudo make install

Installing on FreeBSD

./configure --prefix=$(pwd)/BUILD
make clean
make
make install

Installing on Windows 32-bit

./configure --host=i686-w64-mingw32 --prefix=$(pwd)/build-w32
make clean
make
make install

Installing on Windows 64-bit

./configure --host=x86_64-w64-mingw32 --prefix=$(pwd)/build-w64
make clean
make
make install

NOTE: Some Distros and Operating Systems may require you to run 'ldconfig' to recognize libspiro if you are not rebooting your computer first before installing another program that depends on libspiro. To do this, you may need to run 'ldconfig' in 'su -' mode after you have done 'make install':

	$ su -
	# ldconfig
	# exit
	$

Usage

In FontForge

FontForge will autodetect libspiro when it is installed in the usual way.

An exception to this is with the Mac bundled version (where FontForge.app is copied to /Applications). To install your compiled version into the bundle, run sh ./configure --prefix=/Applications/FontForge.app/Contents/Resources/opt/local/

Crash Reporting

Mac OS X: A helping script, ./fontforge.sh is provided to run FontForge inside a debugger to get useful information on solving crashes. An example issue is at #4

Developing

Two methods of using libspiro in your programs

Programming with libspiro in C

Basic Types

The spiro control point

typedef struct {
    double x;
    double y;
    char ty;
} spiro_cp;

    /* Possible values of the "ty" field. */
#define SPIRO_CORNER	'v'
#define SPIRO_G4	'o'
#define SPIRO_G2	'c'
#define SPIRO_LEFT	'['
#define SPIRO_RIGHT	']'
#define SPIRO_ANCHOR	'a'
#define SPIRO_HANDLE	'h'

    /* For a closed contour add an extra cp with a ty set to */
#define SPIRO_END		'z'
    /* For an open contour the first cp must have a ty set to*/
#define SPIRO_OPEN_CONTOUR	'{'
    /* For an open contour the last cp must have a ty set to */
#define SPIRO_END_OPEN_CONTOUR	'}'

A spiro control point contains a location and a point type. There are six basic types of spiro control points:

  • A corner point – where the slopes and curvatures of the incoming and outgoing splines are unconstrained
  • A G4 curve point – Continuous up to the fourth derivative
  • A G2 curve point – Continuous up to the second derivative.
  • A left constraint point – Used to connect a curve to a straight line
  • A right constraint point – Used to connect a straight line to a curve. If you have a contour which is drawn clockwise, and you have a straight segment at the top, then the left point of that straight segment should be a left constraint, and the right point should be a right constraint.
  • An anchor - Is a knot point with a fixed angle (followed by the handle cp which creates the angle). The anchor behaves as both left and right constraint points at one single point.

The left constraint, right constraint, anchor and handle points are easiest explained using examples from path5 and path6 which are tested in tests/call-test5.c and tests/call-test6.c

path5[]		path6[]
{  0,   0,'{'},	{  0,   0,'{'},
{100, 100,'c'},	{100, 100,'c'},
{200, 200,'['},	{200, 200,'a'},
{300, 200,']'},	{300, 200,'h'},
{400, 150,'c'},	{300, 150,'c'},
{300, 100,'['},	{200, 100,'a'},
{200, 100,']'},	{150, 100,'h'},
{150,  50,'c'},	{150,  50,'c'},
{100,   0,'['},	{100,   0,'a'},
{  0,-100,']'},	{  0,-100,'h'},
{-50,-200,'c'},	{ 50,-100,'c'},
{-80,-250,'}'},	{ 20,-150,'}'},

The ncq control value

There is a need to pass additional information to libspiro, and therefore the 'ncq' value was added. 'ncq' can be thought of as toggle switches telling libspiro how to work with the source spiro control points. Below is the current toggle switch definitions, and default 'ncq' value is zero.

/* int ncq flags and values */
#define SPIRO_INCLUDE_LAST_KNOT	0x0100
#define SPIRO_INTERNAL_BEZCTX	0x0200
#define SPIRO_RETRO_VER1	0x0400
#define SPIRO_REVERSE_SRC	0x0800
#define SPIRO_ARC_CUB_QUAD_CLR	0x7FFF
#define SPIRO_ARC_CUB_QUAD_MASK	0x7000
#define SPIRO_CUBIC_TO_BEZIER	0x0000
#define SPIRO_CUBIC_MIN_MAYBE	0x1000
#define SPIRO_ARC_MAYBE		0x2000
#define SPIRO_ARC_MIN_MAYBE	0x3000
#define SPIRO_QUAD0_TO_BEZIER	0x4000

The definitions for ncq (above) are:

SPIRO_INCLUDE_LAST_KNOT: Existing libspiro behaviour is to add a knot point to match each spiro point, but does not include the last knot. This option includes the last knot with the existing output results while the spiro is still open. Closed spiros should refer to the first knot point since the last and first knot are the same.

SPIRO_INTERNAL_BEZCTX: This provides a simpler interface for developers having trouble with the main libspiro interface. With this method, two new functions are also provided and needed here.

  • 1st, the calling program creates an oversized array for the results using function "new_ls_bezctx()",
  • 2nd, the calling program calls libspiro to create a curve result using "TaggedSpiroCPsToBezier2()" or "SpiroCPsToBezier2()",
  • 3rd, the calling program can resize the array to use less memory, or release the array using if it is not needed any more using "free_ls_bezctx()".

File "tests/call-test21.c" has sample code showing how this can be done.

SPIRO_RETRO_VER1: This newer version of libspiro (2019 and later) has modified the way path calculations are made. The reason for this was seen as an advantage, because it allows a user to scale and move spiro paths, which is a common expectation in graphics, and there are other added advantages, such as making the path as part of templates, and more. An effort was made to keep results as close to original as possible, but this was not possible due to scaling factors in the calculations. As the main user for libspiro is FontForge, users such as font designers may see the least change since scaling targets x={0..1000}, y={0..1000}, while other users in graphics may see changes since they can be using scales much larger than 1000. The good news here is 'SPIRO_RETRO_VER1' allows the user to toggle libspiro to use the older calculation method if the user needs backwards compatibility, otherwise, leaving this off allows spiros to use the new calculation method which allows scaling and moving spiro paths. Older programs that use the older libspiro interfaces will see no-change since they use the older calculation method to maintain backwards compatibility.

SPIRO_REVERSE_SRC: There may be a need to reverse the spiro path direction. This option edits the source spiro path, and reverses the information, then proceeds to continue doing libspiro calculations with the reversed path. When libspiro is done calculating bézier output, you will also have a reversed (input) spiro path, therefore save the new spiro path if you need it. This simplifies this process for the calling program to a simple option 'SPIRO_REVERSE_SRC', and the results are up to date as per this version of libspiro. NOTE - libspiro calculations are a one-way calculation, so you are not likely to see the same results in the reverse spiro path direction, but if you need this option, it is available here.

SPIRO_CUBIC_TO_BEZIER: LibSpiro default action is to create cubic bézier curves.

SPIRO_CUBIC_MIN_MAYBE: Cubic arcs can potentially be made with greater bends and less points.

SPIRO_ARC_MAYBE and SPIRO_ARC_MIN_MAYBE: Instead of the default cubic output, this exposes the midpoint, which might be useful to someone.

SPIRO_QUAD0_TO_BEZIER: Rough approximation of quadratic to bézier curves. Knot points will have smooth connection but midpoints may be visually okay or not.

The bezier context

struct _bezctx {
    /* Called by spiro to start a contour */
    void (*moveto)(bezctx *bc, double x, double y, int is_open);

    /* Called by spiro to move from the last point to the next one on a straight line */
    void (*lineto)(bezctx *bc, double x, double y);

    /* Called by spiro to move from the last point to the next along a quadratic bézier spline */
    /* (x1,y1) is the quadratic bézier control point and (x2,y2) will be the new end point */
    void (*quadto)(bezctx *bc, double x1, double y1, double x2, double y2);

    /* Called by spiro to move from the last point to the next along a cubic bézier spline */
    /* (x1,y1) and (x2,y2) are the two off-curve control point and (x3,y3) will be the new end point */
    void (*curveto)(bezctx *bc, double x1, double y1, double x2, double y2,
            double x3, double y3);

    /* Called by spiro to notify calling function this is a knot point */
    void (*mark_knot)(bezctx *bc, int knot_idx);
};

You must create a super-class of this abstract type that handles the creation of your particular representation of bézier splines. As an example I provide the one used by Raph to generate PostScript output (cubic béziers). Spiro will convert a set of spiro_cps into a set of bézier curves. As it does so it will call the appropriate routine in your bézier context with this information – this should allow you to create your own internal representation of those curves.

Calling into libspiro

Your program needs this Libspiro header file:

#include <spiroentrypoints.h>

You must define a bézier context that is appropriate for your internal splines (See Raph's PostScript example).

SpiroCPsToBezier2

You must create an array of spiro control points:

   spiro_cp points[4];

     /* This defines something very like a circle, centered at the origin with radius 100 */

   points[0].x = -100; points[0].y =    0; points[0].ty = SPIRO_G4;
   points[1].x =    0; points[1].y =  100; points[1].ty = SPIRO_G4;
   points[2].x =  100; points[2].y =    0; points[2].ty = SPIRO_G4;
   points[3].x =    0; points[3].y = -100; points[3].ty = SPIRO_G4;

Then call SpiroCPsToBezier2, a routine which takes 5 arguments and returns bc and an integer pass/fail flag.

  1. An array of input spiros
  2. The number of elements in the spiros array (this example has 4)
  3. Additional ncq control variable (default==0)
  4. Whether this describes a closed (True=1) or open (False=0) contour
  5. A bézier results output context
  6. An integer success flag. 1 = completed task and have valid bézier results, or 0 = unable to complete task, bézier results are invalid.
  bc = new_bezctx_ps();
  success = SpiroCPsToBezier2(points,4,ncq,True,bc)
  bezctx_ps_close(bc);

TaggedSpiroCPsToBezier2

Or call TaggedSpiroCPsToBezier2. This routine requires that the array of spiro control points be tagged according to Raph's internal conventions. A closed curve will have an extra control point attached to the end of it with a type of SPIRO_END;

   spiro_cp points[5];

   points[0].x = -100; points[0].y =    0; points[0].ty = SPIRO_G4;
   points[1].x =    0; points[1].y =  100; points[1].ty = SPIRO_G4;
   points[2].x =  100; points[2].y =    0; points[2].ty = SPIRO_G4;
   points[3].x =    0; points[3].y = -100; points[3].ty = SPIRO_G4;
   points[4].x =    0; points[4].y =    0; points[4].ty = SPIRO_END;

(The x,y location of this last SPIRO_END point is irrelevant).

An open curve will have the type of the first control point set to SPIRO_OPEN_CONTOUR and the last to SPIRO_END_OPEN_CONTOUR.

   spiro_cp points[4];

   points[0].x = -100; points[0].y =    0; points[0].ty = SPIRO_OPEN_CONTOUR;
   points[1].x =    0; points[1].y =  100; points[1].ty = SPIRO_G4;
   points[2].x =  100; points[2].y =    0; points[2].ty = SPIRO_G4;
   points[3].x =    0; points[3].y = -100; points[3].ty = SPIRO_END_OPEN_CONTOUR;

(In an open contour the point types of the first and last control points are going to be ignored).

In this case there is no need to provide a point count nor an open/closed contour flag. That information can be obtained from the control points themselves. So TaggedSpiroCPsToBezier2 only takes 3 arguments and returns bc and an integer pass/fail flag.

  1. An array of input spiros

  2. A bézier results output context

  3. Additional ncq control variable (default==0)

  4. An integer success flag. 1 = completed task and have valid bézier results, or 0 = unable to complete task, bézier results are invalid.

     bc = new_bezctx_ps();
     success = TaggedSpiroCPsToBezier2(points,ncq,bc)
     bezctx_ps_close(bc);

Programming with libspiro in Java

CAVEAT: I'm not proficient in Java.

Classes

  • SpiroPointType – this is an enumerated type which defines the same pointtypes used by the C interface: CORNER, G4, G2, LEFT, RIGHT, END, OPEN, OPEN_END

  • SpiroCP

    public class SpiroCP {
      public double x,y;
      SpiroPointType type;
      public SpiroCP(double xx, double yy, SpiroPointType ty);
      public String toString();
    }
  • SpiroBezierContext – a Java interface used in conversion of an array of SpiroCPs to a Bézier contour.

    public interface SpiroBezierContext {
      void MoveTo(double x, double y, boolean isOpen);
      void LineTo(double x, double y);
      void QuadTo(double x1, double y1, double x2, double y2);
      void CubicTo(double x1, double y1, double x2, double y2, double x3, double y3);
      void MarkKnot(int knotIdx);
    }
  • Spiro – a class with only static members:

    public class Spiro {
      // takes an array of SpiroCPs and converts to a Bézier
      static public void
        SpiroCPsToBezier(SpiroCP [] spiros,int n,boolean isclosed,
            SpiroBezierContext bc);
      // takes an array of SpiroCPs (the array contains its own terminator and indication of whether the contour is open or closed)
      static public void
        TaggedSpiroCPsToBezier(SpiroCP [] spiros,SpiroBezierContext bc);
    
      // Two routines for reading and writing one of Raph's plate files
      static public void
        SavePlateFile(Writer output,SpiroCP [][] spirocontours)
        throws IOException;
      static public SpiroCP [][]
        ReadPlateFile(BufferedReader input) throws IOException;
    }

libspiro's People

Contributors

artoria2e5 avatar davelab6 avatar jamadagni avatar joescat avatar jtanx avatar orbea avatar wieslawsoltes avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

libspiro's Issues

Can't compile for x86_64

Git master head won't compile for me:

dcrossland-macbookpro4:libspiro dcrossland$ autoreconf -i
glibtoolize: putting auxiliary files in `.'.
glibtoolize: copying file `./ltmain.sh'
glibtoolize: putting macros in AC_CONFIG_MACRO_DIR, `m4'.
glibtoolize: copying file `m4/libtool.m4'
glibtoolize: copying file `m4/ltoptions.m4'
glibtoolize: copying file `m4/ltsugar.m4'
glibtoolize: copying file `m4/ltversion.m4'
glibtoolize: copying file `m4/lt~obsolete.m4'
configure.ac:35: warning: AM_INIT_AUTOMAKE: two- and three-arguments forms are deprecated.  For more info, see:
configure.ac:35: http://www.gnu.org/software/automake/manual/automake.html#Modernize-AM_005fINIT_005fAUTOMAKE-invocation
configure.ac:38: installing './compile'
configure.ac:35: installing './install-sh'
configure.ac:35: installing './missing'
Makefile.am: installing './depcomp'
parallel-tests: installing './test-driver'
dcrossland-macbookpro4:libspiro dcrossland$ automake --foreign -Wall
configure.ac:35: warning: AM_INIT_AUTOMAKE: two- and three-arguments forms are deprecated.  For more info, see:
configure.ac:35: http://www.gnu.org/software/automake/manual/automake.html#Modernize-AM_005fINIT_005fAUTOMAKE-invocation
/usr/local/Cellar/automake/1.14/share/automake-1.14/am/ltlibrary.am: warning: 'libspiro.la': linking libtool libraries using a non-POSIX
/usr/local/Cellar/automake/1.14/share/automake-1.14/am/ltlibrary.am: archiver requires 'AM_PROG_AR' in 'configure.ac'
Makefile.am:13:   while processing Libtool library 'libspiro.la'
dcrossland-macbookpro4:libspiro dcrossland$ ./configure 
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... ./install-sh -c -d
checking for gawk... no
checking for mawk... no
checking for nawk... no
checking for awk... awk
checking whether make sets $(MAKE)... yes
checking whether make supports nested variables... yes
checking build system type... x86_64-apple-darwin12.4.0
checking host system type... x86_64-apple-darwin12.4.0
checking how to print strings... printf
checking for style of include used by make... GNU
checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables... 
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking whether gcc understands -c and -o together... yes
checking dependency style of gcc... gcc3
checking for a sed that does not truncate output... /usr/bin/sed
checking for grep that handles long lines and -e... /usr/bin/grep
checking for egrep... /usr/bin/grep -E
checking for fgrep... /usr/bin/grep -F
checking for ld used by gcc... /usr/llvm-gcc-4.2/libexec/gcc/i686-apple-darwin11/4.2.1/ld
checking if the linker (/usr/llvm-gcc-4.2/libexec/gcc/i686-apple-darwin11/4.2.1/ld) is GNU ld... no
checking for BSD- or MS-compatible name lister (nm)... /usr/bin/nm
checking the name lister (/usr/bin/nm) interface... BSD nm
checking whether ln -s works... yes
checking the maximum length of command line arguments... 196608
checking whether the shell understands some XSI constructs... yes
checking whether the shell understands "+="... yes
checking how to convert x86_64-apple-darwin12.4.0 file names to x86_64-apple-darwin12.4.0 format... func_convert_file_noop
checking how to convert x86_64-apple-darwin12.4.0 file names to toolchain format... func_convert_file_noop
checking for /usr/llvm-gcc-4.2/libexec/gcc/i686-apple-darwin11/4.2.1/ld option to reload object files... -r
checking for objdump... no
checking how to recognize dependent libraries... pass_all
checking for dlltool... no
checking how to associate runtime and link libraries... printf %s\n
checking for ar... ar
checking for archiver @FILE support... no
checking for strip... strip
checking for ranlib... ranlib
checking command to parse /usr/bin/nm output from gcc object... ok
checking for sysroot... no
checking for mt... no
checking if : is a manifest tool... no
checking for dsymutil... dsymutil
checking for nmedit... nmedit
checking for lipo... lipo
checking for otool... otool
checking for otool64... no
checking for -single_module linker flag... yes
checking for -exported_symbols_list linker flag... yes
checking for -force_load linker flag... yes
checking how to run the C preprocessor... gcc -E
checking for ANSI C header files... yes
checking for sys/types.h... yes
checking for sys/stat.h... yes
checking for stdlib.h... yes
checking for string.h... yes
checking for memory.h... yes
checking for strings.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for unistd.h... yes
checking for dlfcn.h... yes
checking for objdir... .libs
checking if gcc supports -fno-rtti -fno-exceptions... no
checking for gcc option to produce PIC... -fno-common -DPIC
checking if gcc PIC flag -fno-common -DPIC works... yes
checking if gcc static flag -static works... no
checking if gcc supports -c -o file.o... yes
checking if gcc supports -c -o file.o... (cached) yes
checking whether the gcc linker (/usr/llvm-gcc-4.2/libexec/gcc/i686-apple-darwin11/4.2.1/ld) supports shared libraries... yes
checking dynamic linker characteristics... darwin12.4.0 dyld
checking how to hardcode library paths into programs... immediate
checking whether stripping libraries is possible... yes
checking if libtool supports shared libraries... yes
checking whether to build shared libraries... yes
checking whether to build static libraries... yes
checking for gcc... (cached) gcc
checking whether we are using the GNU C compiler... (cached) yes
checking whether gcc accepts -g... (cached) yes
checking for gcc option to accept ISO C89... (cached) none needed
checking whether gcc understands -c and -o together... (cached) yes
checking dependency style of gcc... (cached) gcc3
checking whether ln -s works... yes
checking whether make sets $(MAKE)... (cached) yes
checking for a sed that does not truncate output... (cached) /usr/bin/sed
checking for wget... /usr/local/bin/wget
checking whether make supports nested variables... (cached) yes
checking for finite... yes
checking pthread.h usability... yes
checking pthread.h presence... yes
checking for pthread.h... yes
checking whether C compiler accepts -Wall... yes
checking whether C compiler accepts -Wextra... yes
checking whether C compiler accepts -Wcast-align... yes
checking whether C compiler accepts -Wbad-function-cast... yes
checking whether C compiler accepts -Wc++-compat... yes
checking whether C compiler accepts -Wmissing-prototypes... yes
checking whether C compiler accepts -Wunused... yes
checking that generated files are newer than configure... done
configure: creating ./config.status
config.status: creating Makefile
config.status: creating tests/Makefile
config.status: creating libspiro.pc
config.status: creating spiro-config.h
config.status: executing depfiles commands
config.status: executing libtool commands
dcrossland-macbookpro4:libspiro dcrossland$ make
make  all-recursive
Making all in .
  CC       spiro.lo
spiro.c: In function 'check_finiteness':
spiro.c:820: warning: implicit declaration of function '__finite'
  CC       bezctx.lo
  CC       spiroentrypoints.lo
  CCLD     libspiro.la
Undefined symbols for architecture x86_64:
  "___finite", referenced from:
      _run_spiro in spiro.o
ld: symbol(s) not found for architecture x86_64
collect2: ld returned 1 exit status
make[2]: *** [libspiro.la] Error 1
make[1]: *** [all-recursive] Error 1
make: *** [all] Error 2

Rust port

Hello Joe:

As part of my new font editor project (currently only a glyph editor 😉), I created a Spiro binding in Rust.

https://github.com/mfeq/spiro-sys

I'll also be making what Rust users call a "safe" version; basically, this is a version which wraps the memory-unsafe functions of libspiro to provide a (more) memory-safe API.

I noticed while doing this that the README's drawing of path 5 seems wrong. I got this instead:

Could very well be my fault, but wanted to ask you.

The code that generates that is here:

https://github.com/mfeq/spiro-sys/blob/22b9e1e9e85f4471910876cf4232edec4dbd8441/tests/spiro_to_beziers.rs#L27-L57

Crash in git master

:)

$ ./fontforge.sh 
Script started, output file is /Users/dcrossland/FontForge-Debug-Output.txt
(lldb) Executing commands in 'debug-script.sh'.
(lldb)  #!/bin/sh
(lldb)  version
LLDB-179.5
(lldb)  settings set frame-format "frame #${frame.index}: ${frame.pc}{ ${module.file.basename}`${function.name-with-args}{${function.pc-offset}}}{ at ${line.file.basename}:${line.number}}\n"
(lldb)  target create /usr/local/bin/fontforge
Current executable set to '/usr/local/bin/fontforge' (x86_64).
(lldb)  target select 0
Current targets:
* target #0: /usr/local/bin/fontforge ( arch=x86_64-apple-macosx, platform=localhost )
(lldb)  run
Process 18877 launched: '/usr/local/bin/fontforge' (x86_64)
Command #6 'run' continued the target.
Copyright (c) 2000-2012 by George Williams.
 Executable based on sources from 14:57 GMT 31-Jul-2012-D.
 Library based on sources from 14:57 GMT 31-Jul-2012.
Recovering from /Users/dcrossland/.FontForge/autosave/auto003ec1-1.asfd...  Done
Recovering from /Users/dcrossland/.FontForge/autosave/auto0048b8-1.asfd...  Done
Recovering from /Users/dcrossland/.FontForge/autosave/auto0048cc-1.asfd...  Done
Recovering from /Users/dcrossland/.FontForge/autosave/auto0048d5-1.asfd...  Done
Process 18877 stopped
* thread #1: tid = 0x1c03, 0x00000001003a82df libfontforge.1.dylib`SpiroCP2SplineSet + 316, stop reason = EXC_BAD_ACCESS (code=1, address=0x18)
    "frame #0: 0x00000001003a82df libfontforge.1.dylib`SpiroCP2SplineSet + 316
"libfontforge.1.dylib`SpiroCP2SplineSet + 316:
-> 0x1003a82df:  movq   %r14, 24(%r15)
   0x1003a82e3:  incl   %ebx
   0x1003a82e5:  movw   %bx, 34(%r15)
   0x1003a82ea:  movw   %bx, 32(%r15)
(lldb) bt
* thread #1: tid = 0x1c03, 0x00000001003a82df libfontforge.1.dylib`SpiroCP2SplineSet + 316, stop reason = EXC_BAD_ACCESS (code=1, address=0x18)
    "frame #0: 0x00000001003a82df libfontforge.1.dylib`SpiroCP2SplineSet + 316
"    "frame #1: 0x00000001003a8678 libfontforge.1.dylib`SSRegenerateFromSpiros + 46
"    "frame #2: 0x000000010004de6c fontforge`CVMouseMoveSpiroPoint + 332
"    "frame #3: 0x0000000100034562 fontforge`CVMouseMove + 2278
"    "frame #4: 0x000000010003050c fontforge`v_e_h + 234
"    "frame #5: 0x00000001005b70f5 libgdraw.4.dylib`_GWidget_Container_eh + 1541
"    "frame #6: 0x000000010060328c libgdraw.4.dylib`dispatchEvent + 5518
"    "frame #7: 0x0000000100600c86 libgdraw.4.dylib`GXDrawEventLoop + 65
"    "frame #8: 0x0000000100126868 fontforge`main + 5982
"    "frame #9: 0x00007fff839227e1 libdyld.dylib`start + 1
"(lldb) q

Release?

@JoesCat are you happy to cut a new release? I'm planning on switching the Windows builds of FontForge to just use the main msys2 package repo instead of having to maintain my own for libspiro/libuninameslist, but this will mean going back to the last tagged version of libspiro.

Can't compile on xubuntu-12.04 32-bit

Trying to compile libspiro on a 32-bit xubuntu-12.04 I perform the following steps:
$autoreconf
$automake [have tried with and without --foreign -Wall]
$./configure

then make fails at CCLD call-test:

$make
make all-recursive
make[1]: Entering directory /home/slarty/CODE/source/fontforge/git_fontforge/libspiro' Making all in . make[2]: Entering directory/home/slarty/CODE/source/fontforge/git_fontforge/libspiro'
CC spiro.lo
CC bezctx.lo
CC spiroentrypoints.lo
CCLD libspiro.la
make[2]: Leaving directory /home/slarty/CODE/source/fontforge/git_fontforge/libspiro' Making all in tests make[2]: Entering directory/home/slarty/CODE/source/fontforge/git_fontforge/libspiro/tests'
CC unit-test.o
CCLD unit-test
CC call-test.o
CCLD call-test
../.libs/libspiro.so: undefined reference to sincos' ../.libs/libspiro.so: undefined reference toatan2'
../.libs/libspiro.so: undefined reference to hypot' collect2: ld returned 1 exit status make[2]: *** [call-test] Error 1 make[2]: Leaving directory/home/slarty/CODE/source/fontforge/git_fontforge/libspiro/tests'
make[1]: *** [all-recursive] Error 1
make[1]: Leaving directory `/home/slarty/CODE/source/fontforge/git_fontforge/libspiro'
make: *** [all] Error 2


I assume there is a missing -lm somewhere but have not successfully found a solution.
I have however successfully compiled and installed libspiro from here:
http://libspiro.sourceforge.net

Cannot compile using parallel make

There are issues when compiling with parallel make (e.g make -j4)

Without this option, it compiles fine.

make  all-recursive
make[1]: Entering directory '/home/jeremy/ff-build/work/libspiro'
Making all in .
make[2]: Entering directory '/home/jeremy/ff-build/work/libspiro'
  CC       bezctx.lo
  CC       spiro.lo
  CC       spiroentrypoints.lo
  CCLD     libspiro.la
make[2]: Leaving directory '/home/jeremy/ff-build/work/libspiro'
Making all in tests
make[2]: Entering directory '/home/jeremy/ff-build/work/libspiro/tests'
  CC       call-test.o
  CC       unit-test.o
  CCLD     call-test.exe
  CCLD     unit-test.exe
i686-w64-mingw32-gcc.exe: error: unrecognized command line option '--mode=link'
Makefile:553: recipe for target 'unit-test.exe' failed
make[2]: *** [unit-test.exe] Error 1
make[2]: *** Waiting for unfinished jobs....
i686-w64-mingw32-gcc.exe: error: unrecognized command line option '--mode=link'
Makefile:549: recipe for target 'call-test.exe' failed
make[2]: *** [call-test.exe] Error 1
make[2]: Leaving directory '/home/jeremy/ff-build/work/libspiro/tests'
Makefile:565: recipe for target 'all-recursive' failed
make[1]: *** [all-recursive] Error 1
make[1]: Leaving directory '/home/jeremy/ff-build/work/libspiro'
Makefile:403: recipe for target 'all' failed
make: *** [all] Error 2

configure error

wget -O libspiro-20221101.tar.gz https://github.com/fontforge/libspiro/archive/refs/tags/20221101.tar.gz
tar xvf libspiro-20221101.tar.gz
autoreconf -fvi

error line 14517: syntax error near unexpected token `done', where I see two cycles "for ac_func in " for inside each other
where uses the same var ac_func
except this, in first cycle:
else
case e in
e)

and in nested if again
else
case e in
e)

in whole, syntax is correct, I don't understand where is a mistake
but the same time, code too comple to find bug

checking whether the gcc linker (/usr/local/bin/ld -m elf_x86_64) supports shared libraries... yes
checking whether -lc should be explicitly linked in... no
checking dynamic linker characteristics... GNU/Linux ld.so
checking how to hardcode library paths into programs... immediate
checking whether stripping libraries is possible... yes
checking if libtool supports shared libraries... yes
checking whether to build shared libraries... yes
checking whether to build static libraries... no
checking for gcc... (cached) gcc
checking whether the compiler supports GNU C... (cached) yes
checking whether gcc accepts -g... (cached) yes
checking for gcc option to enable C11 features... (cached) none needed
checking whether gcc understands -c and -o together... (cached) yes
checking dependency style of gcc... (cached) gcc3
checking whether ln -s works... yes
checking whether make sets $(MAKE)... (cached) yes
checking for a sed that does not truncate output... (cached) /usr/local/bin/sed
checking for valgrind... valgrind
checking for strip... (cached) strip
checking whether make supports nested variables... (cached) yes
checking for math.h... yes
./configure: line 14517: syntax error near unexpected token done' ./configure: line 14517: done'

Linux Mint 20.3 Cinnamon
x86_64 - kernel 5.15

Stack-based buffer overflow in the spiro_to_bpath0() function

Hi,

When building libspiro 20190731 with AddressSanitizer enabled and running the test suite, I found a stack-based buffer overflow in spiro_to_bpath0() in spiro.c.

In the test suite, call-test14 to call-test19 all trigger the same issue.

The issue can be triggered as follow:

./configure CFLAGS="-fsanitize=address"
make
./tests/call-test14
=================================================================
==1829==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffcf5a2c938 at pc 0x7fdc1174d487 bp 0x7ffcf5a2c450 sp 0x7ffcf5a2c448
WRITE of size 8 at 0x7ffcf5a2c938 thread T0
    #0 0x7fdc1174d486 in spiro_to_bpath0 /home/fcambus/libspiro-20190731/spiro.c:1182:11
    #1 0x4c9ea0 in test_curve /home/fcambus/libspiro-20190731/tests/./call-test.c:666:5
    #2 0x4ca119 in main /home/fcambus/libspiro-20190731/tests/./call-test.c:1172:9
    #3 0x7fdc1152e1e2 in __libc_start_main /build/glibc-4WA41p/glibc-2.30/csu/../csu/libc-start.c:308:16
    #4 0x41b31d in _start (/home/fcambus/libspiro-20190731/tests/.libs/call-test14+0x41b31d)

Address 0x7ffcf5a2c938 is located in stack of thread T0 at offset 632 in frame
    #0 0x4c832f in test_curve /home/fcambus/libspiro-20190731/tests/./call-test.c:536

  This frame has 3 object(s):
    [32, 416) 'spiro' (line 537)
    [480, 548) 'nextknot' (line 538)
    [592, 632) 'd' (line 539) <== Memory access at offset 632 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow /home/fcambus/libspiro-20190731/spiro.c:1182:11 in spiro_to_bpath0
Shadow bytes around the buggy address:
  0x10001eb3d8d0: 00 00 00 00 00 00 00 00 f1 f1 f1 f1 00 00 00 00
  0x10001eb3d8e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10001eb3d8f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10001eb3d900: 00 00 00 00 00 00 00 00 00 00 00 00 f2 f2 f2 f2
  0x10001eb3d910: f2 f2 f2 f2 00 00 00 00 00 00 00 00 04 f2 f2 f2
=>0x10001eb3d920: f2 f2 00 00 00 00 00[f3]f3 f3 f3 f3 00 00 00 00
  0x10001eb3d930: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10001eb3d940: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10001eb3d950: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10001eb3d960: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10001eb3d970: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==1829==ABORTING

Update license headers

According to COPYING, libspiro is released under the term of GPLv3+, but in the license headers on top of *.c, *.java and bezctx.md are still saying GPLv2+.

It seems necessary to update license headers too.

Please consider using `static const`'s for SPIRO_CORNER, SPIRO_G4, SPIRO_G2, etc.

Let me begin by saying, this is not really your problem, but rather bindgen's problem. (bindgen does the C interop for Rust, one of the main reasons I think Rust is worth using; without bindgen giving C ABI access it's not that great.)

But, I think it would be good to consider changing it anyway. 🥺😉

Having these as symbols in the library is better than just #define's and makes it easier for bindings to "see" them and work with them. There is also the issue that bindgen really cannot know if 'v' is a signed char or an unsigned char; it is ambiguous. In C this is not a problem. In Rust, it is a headache to convert i8 (c_char) to u8 ('v', etc.).

If you agree @JoesCat, I'm happy to PR this.

Patch: MFEK@bb20451

'finite' is deprecated

When compiling on OS X, it reports a deprecation warning:

../spiro.c:896:11: warning: 'finite' is deprecated: first deprecated in macOS 10.9 - Use `isfinite((double)x)` instead.
      [-Wdeprecated-declarations]
            if ( IS_FINITE( segs[i].ks[j])==0 ) return -1;

The comment for finite says what should be done:

Legacy BSD API; use the C99 isfinite( ) macro instead.

Looks like the HAVE_FINITE macro detection needs to be tweaked for OS X.

Please consider changing bezctx* to void* most places

(Copied from MFEK/libspiro@772785b README.md, which assumes this was already done. Of course it's open for discussion, I'm just too lazy to retype. 😄)

Before 20200918, the Bézier context was of type bezctx*, meaning, a pointer to a _bezctx struct. (typedef struct _bezctx bezctx;)

Afterwards, it was changed to a void*, meaning, any pointer. As C is not object oriented and does not have classes, there is no better alternative.

The main reasons this was done is that Rust's bindgen was applying Rust's type checking to the struct, thinking it was really supposed to be a bezctx and nothing else.

However, void* is also more correct. As stated above, in almost all applications (any application that needs to store the produced Bézier and not just immediately write it out), a typecast is going to be needed to silence compiler warnings. In almost all applications, it makes no sense, therefore, for the type to be bezctx*, as the programmer will be typecasting. Instead of defining a concrete type, then, allowing any type is better.

Of course, though, to prevent segmentation faults, the type you use must contain all of the fields, in the same order, as a bezctx*, whether directly in your subordinate struct, or else in a base item.

If you were not already typecasting for some reason (perhaps you made, for example, an SVG writer), you can fix your callback code to link with the new version of the library by changing the first argument in the signatures of all your custom callback functions (i.e. bezctx_moveto, bezctx_lineto, etc.) from bezctx *bc to void* _bc. Then, on the first one of each callback function, do:

    bezctx* bc = (bezctx*)_bc;

Example:

diff --git a/bezctx.c b/bezctx.c
index 94979ff..9f55eef 100644
--- a/bezctx.c
+++ b/bezctx.c
@@ -25,41 +25,46 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA

 #include "bezctx.h"

-void bezctx_moveto(bezctx *bc, double x, double y, int is_open)
+void bezctx_moveto(void *_bc, double x, double y, int is_open)
 {
+    bezctx* bc = (bezctx*)_bc;
 #ifdef VERBOSE
     printf("moveto(%g,%g)_%d\n",x,y,is_open);
 #endif
     bc->moveto(bc, x, y, is_open);
 }

Patch: MFEK@772785b

mipsel build issues

Hi,

there are some issues in the mipsel build at Debian.
Build log: https://buildd.debian.org/status/fetch.php?pkg=libspiro&arch=mipsel&ver=1%3A0.3.20150131-2&stamp=1430325106

The test-suite.log file:

==============================================
   spiro 0.3.20150131: tests/test-suite.log
==============================================

# TOTAL: 2
# PASS:  1
# SKIP:  0
# XFAIL: 0
# FAIL:  1
# XPASS: 0
# ERROR: 0

.. contents:: :depth: 2

FAIL: call-test
===============

testing run_spiro() using data=path0[].
curve 0, line 0, x=334.000000 y=117.000000 t=v bend=0.000000 ch=65.741920 th=2.027641 l=0.000000 
curve 0, line 1, x=305.000000 y=176.000000 t=v bend=0.000000 ch=99.020200 th=-2.791096 l=0.000000 
curve 0, line 2, x=212.000000 y=142.000000 t=c bend=-0.851164 ch=60.415230 th=2.640925 l=0.000000 
curve 0, line 3, x=159.000000 y=171.000000 t=c bend=-1.847893 ch=92.633687 th=0.793032 l=0.000000 
curve 0, line 4, x=224.000000 y=237.000000 t=c bend=-0.120277 ch=157.267288 th=0.672755 l=0.000000 
curve 0, line 5, x=347.000000 y=335.000000 t=c bend=1.730337 ch=196.084166 th=2.403091 l=0.000000 
curve 0, line 6, x=202.000000 y=467.000000 t=c bend=1.042797 ch=126.826653 th=-2.837297 l=0.000000 
curve 0, line 7, x=81.000000 y=429.000000 t=v bend=0.000000 ch=69.354164 th=-1.074902 l=0.000000 
curve 0, line 8, x=114.000000 y=368.000000 t=v bend=0.000000 ch=93.407708 th=0.372554 l=0.000000 
curve 0, line 9, x=201.000000 y=402.000000 t=c bend=-0.787061 ch=81.939002 th=-0.414507 l=0.000000 
curve 0, line 10, x=276.000000 y=369.000000 t=c bend=-1.916483 ch=84.172442 th=-2.330990 l=0.000000 
curve 0, line 11, x=218.000000 y=308.000000 t=c bend=-0.158341 ch=159.806133 th=-2.489331 l=0.000000 
curve 0, line 12, x=91.000000 y=211.000000 t=c bend=1.237282 ch=105.304321 th=-1.252049 l=0.000000 
curve 0, line 13, x=124.000000 y=111.000000 t=c bend=0.982576 ch=108.931171 th=-0.269473 l=0.000000 
curve 0, line 14, x=229.000000 y=82.000000 t=c bend=-2.528266 ch=243.238566 th=-2.797739 l=0.000000 
curve 0, line 15, x=0.000000 y=0.000000 t=z bend=3.134681 ch=353.899703 th=0.336942 l=0.000000 
testing spiro_to_bpath() using data from run_spiro(data=path0[],len=16).
test_moveto(334,117)_0
test_mark_knot()_0
test_lineto(305,176)
test_mark_knot()_1
test_curveto(278.711,155.106, 245.566,142.988, 212,142)
test_mark_knot()_2
test_curveto(201.253,142.281, 190.34,143.726, 180.596,148.267)
test_curveto(175.724,150.538, 171.185,153.578, 167.419,157.413)
test_curveto(163.652,161.248, 160.67,165.891, 159,171)
test_mark_knot()_3
test_curveto(156.308,179.236, 157.16,188.418, 160.551,196.391)
test_curveto(163.942,204.365, 169.745,211.173, 176.569,216.513)
test_curveto(190.217,227.191, 207.414,231.978, 224,237)
test_mark_knot()_4
test_curveto(250.342,244.977, 276.682,254.316, 299.357,269.917)
test_curveto(322.032,285.518, 340.938,308.152, 347,335)
test_mark_knot()_5
test_curveto(351.153,353.392, 349.045,373.004, 341.926,390.463)
test_curveto(334.806,407.923, 322.8,423.235, 308.112,435.059)
test_curveto(278.737,458.706, 239.7,467.886, 202,467)
test_mark_knot()_6
test_curveto(159.176,465.994, 116.711,452.658, 81,429)
test_mark_knot()_7
test_lineto(114,368)
test_mark_knot()_8
test_curveto(140.313,385.041, 170.105,396.684, 201,402)
test_mark_knot()_9
test_curveto(216.033,404.587, 231.809,405.634, 246.268,400.776)
test_curveto(253.497,398.347, 260.287,394.428, 265.629,388.985)
test_curveto(270.971,383.541, 274.812,376.534, 276,369)
test_mark_knot()_10
test_curveto(277.186,361.482, 275.702,353.665, 272.413,346.801)
test_curveto(269.124,339.938, 264.092,333.997, 258.267,329.099)
test_curveto(246.617,319.303, 232.105,313.721, 218,308)
test_mark_knot()_11
test_curveto(192.746,297.756, 167.726,286.443, 145.32,270.931)
test_curveto(122.913,255.419, 103.089,235.424, 91,211)
test_mark_knot()_12
test_curveto(81.9232,192.662, 77.4184,171.323, 82.5427,151.513)
test_curveto(85.1048,141.608, 90.0758,132.265, 97.2634,124.984)
test_curveto(104.451,117.703, 113.889,112.559, 124,111)
test_mark_knot()_13
test_curveto(137.228,108.96, 150.603,112.998, 163.11,117.762)
test_curveto(175.618,122.526, 188.071,128.128, 201.395,129.403)
test_curveto(208.057,130.041, 214.889,129.554, 221.213,127.364)
test_curveto(227.537,125.174, 233.332,121.213, 237.179,115.737)
test_curveto(241.025,110.261, 242.792,103.23, 241.403,96.6836)
test_curveto(240.708,93.4104, 239.243,90.2939, 237.104,87.7207)
test_curveto(234.965,85.1475, 232.15,83.128, 229,82)
test_mark_knot()_14
test_lineto(0,0)
test_mark_knot()_15
test_lineto(334,117)

---
testing TaggedSpiroCPsToBezier0() using data=path0[].
test_moveto(334,117)_0
test_mark_knot()_0
test_lineto(305,176)
test_mark_knot()_1
test_curveto(279.449,153.974, 245.733,141.648, 212,142)
test_mark_knot()_2
test_curveto(201.226,142.112, 190.267,143.529, 180.511,148.103)
test_curveto(175.633,150.39, 171.099,153.461, 167.348,157.327)
test_curveto(163.596,161.194, 160.641,165.869, 159,171)
test_mark_knot()_3
test_curveto(156.364,179.243, 157.251,188.403, 160.654,196.36)
test_curveto(164.057,204.316, 169.853,211.112, 176.664,216.45)
test_curveto(190.287,227.126, 207.444,231.953, 224,237)
test_mark_knot()_4
test_curveto(250.322,245.025, 276.649,254.369, 299.324,269.96)
test_curveto(321.998,285.551, 340.921,308.162, 347,335)
test_mark_knot()_5
test_curveto(351.166,353.392, 349.066,373.009, 341.95,390.473)
test_curveto(334.833,407.936, 322.824,423.251, 308.134,435.075)
test_curveto(278.752,458.724, 239.706,467.893, 202,467)
test_mark_knot()_6
test_curveto(159.176,465.986, 116.715,452.651, 81,429)
test_mark_knot()_7
test_lineto(114,368)
test_mark_knot()_8
test_curveto(140.251,385.152, 170.075,396.808, 201,402)
test_mark_knot()_9
test_curveto(216.006,404.519, 231.742,405.476, 246.158,400.607)
test_curveto(253.366,398.172, 260.137,394.269, 265.488,388.861)
test_curveto(270.839,383.452, 274.72,376.5, 276,369)
test_mark_knot()_10
test_curveto(277.287,361.46, 275.914,353.582, 272.701,346.641)
test_curveto(269.488,339.699, 264.498,333.665, 258.672,328.709)
test_curveto(247.02,318.796, 232.367,313.255, 218,308)
test_mark_knot()_11
test_curveto(191.978,298.481, 165.57,289.162, 142.279,274.152)
test_curveto(118.988,259.143, 98.7273,237.609, 91,211)
test_mark_knot()_12
test_curveto(85.821,193.166, 86.5807,173.749, 92.5866,156.176)
test_curveto(98.5925,138.603, 109.749,122.908, 124,111)
test_mark_knot()_13
test_curveto(138.28,99.0677, 155.539,90.9339, 173.578,86.3634)
test_curveto(191.617,81.7928, 210.435,80.7131, 229,82)
test_mark_knot()_14
test_curveto(266.199,84.5786, 302.694,96.7435, 334,117)

---
testing SpiroCPsToBezier0() using data=path0[].
test_moveto(334,117)_0
test_mark_knot()_0
test_lineto(305,176)
test_mark_knot()_1
test_curveto(278.711,155.106, 245.566,142.988, 212,142)
test_mark_knot()_2
test_curveto(201.253,142.281, 190.34,143.726, 180.596,148.267)
test_curveto(175.724,150.538, 171.185,153.578, 167.419,157.413)
test_curveto(163.652,161.248, 160.67,165.891, 159,171)
test_mark_knot()_3
test_curveto(156.308,179.236, 157.16,188.418, 160.551,196.391)
test_curveto(163.942,204.365, 169.745,211.173, 176.569,216.513)
test_curveto(190.217,227.191, 207.414,231.978, 224,237)
test_mark_knot()_4
test_curveto(250.342,244.977, 276.682,254.316, 299.357,269.917)
test_curveto(322.032,285.518, 340.938,308.152, 347,335)
test_mark_knot()_5
test_curveto(351.153,353.392, 349.045,373.004, 341.926,390.463)
test_curveto(334.806,407.923, 322.8,423.235, 308.112,435.059)
test_curveto(278.737,458.706, 239.7,467.886, 202,467)
test_mark_knot()_6
test_curveto(159.176,465.994, 116.711,452.658, 81,429)
test_mark_knot()_7
test_lineto(114,368)
test_mark_knot()_8
test_curveto(140.313,385.041, 170.105,396.684, 201,402)
test_mark_knot()_9
test_curveto(216.033,404.587, 231.809,405.634, 246.268,400.776)
test_curveto(253.497,398.347, 260.287,394.428, 265.629,388.985)
test_curveto(270.971,383.541, 274.812,376.534, 276,369)
test_mark_knot()_10
test_curveto(277.186,361.482, 275.702,353.665, 272.413,346.801)
test_curveto(269.124,339.938, 264.092,333.997, 258.267,329.099)
test_curveto(246.617,319.303, 232.105,313.721, 218,308)
test_mark_knot()_11
test_curveto(192.746,297.756, 167.726,286.443, 145.32,270.931)
test_curveto(122.913,255.419, 103.089,235.424, 91,211)
test_mark_knot()_12
test_curveto(81.9232,192.662, 77.4184,171.323, 82.5427,151.513)
test_curveto(85.1048,141.608, 90.0758,132.265, 97.2634,124.984)
test_curveto(104.451,117.703, 113.889,112.559, 124,111)
test_mark_knot()_13
test_curveto(137.228,108.96, 150.603,112.998, 163.11,117.762)
test_curveto(175.618,122.526, 188.071,128.128, 201.395,129.403)
test_curveto(208.057,130.041, 214.889,129.554, 221.213,127.364)
test_curveto(227.537,125.174, 233.332,121.213, 237.179,115.737)
test_curveto(241.025,110.261, 242.792,103.23, 241.403,96.6836)
test_curveto(240.708,93.4104, 239.243,90.2939, 237.104,87.7207)
test_curveto(234.965,85.1475, 232.15,83.128, 229,82)
test_mark_knot()_14
test_lineto(0,0)
test_mark_knot()_15
test_lineto(334,117)

---
testing TaggedSpiroCPsToBezier1() using data=path0[].
test_moveto(334,117)_0
test_mark_knot()_0
test_lineto(305,176)
test_mark_knot()_1
test_curveto(279.449,153.974, 245.733,141.648, 212,142)
test_mark_knot()_2
test_curveto(201.226,142.112, 190.267,143.529, 180.511,148.103)
test_curveto(175.633,150.39, 171.099,153.461, 167.348,157.327)
test_curveto(163.596,161.194, 160.641,165.869, 159,171)
test_mark_knot()_3
test_curveto(156.364,179.243, 157.251,188.403, 160.654,196.36)
test_curveto(164.057,204.316, 169.853,211.112, 176.664,216.45)
test_curveto(190.287,227.126, 207.444,231.953, 224,237)
test_mark_knot()_4
test_curveto(250.322,245.025, 276.649,254.369, 299.324,269.96)
test_curveto(321.998,285.551, 340.921,308.162, 347,335)
test_mark_knot()_5
test_curveto(351.166,353.392, 349.066,373.009, 341.95,390.473)
test_curveto(334.833,407.936, 322.824,423.251, 308.134,435.075)
test_curveto(278.752,458.724, 239.706,467.893, 202,467)
test_mark_knot()_6
test_curveto(159.176,465.986, 116.715,452.651, 81,429)
test_mark_knot()_7
test_lineto(114,368)
test_mark_knot()_8
test_curveto(140.251,385.152, 170.075,396.808, 201,402)
test_mark_knot()_9
test_curveto(216.006,404.519, 231.742,405.476, 246.158,400.607)
test_curveto(253.366,398.172, 260.137,394.269, 265.488,388.861)
test_curveto(270.839,383.452, 274.72,376.5, 276,369)
test_mark_knot()_10
test_curveto(277.287,361.46, 275.914,353.582, 272.701,346.641)
test_curveto(269.488,339.699, 264.498,333.665, 258.672,328.709)
test_curveto(247.02,318.796, 232.367,313.255, 218,308)
test_mark_knot()_11
test_curveto(191.978,298.481, 165.57,289.162, 142.279,274.152)
test_curveto(118.988,259.143, 98.7273,237.609, 91,211)
test_mark_knot()_12
test_curveto(85.821,193.166, 86.5807,173.749, 92.5866,156.176)
test_curveto(98.5925,138.603, 109.749,122.908, 124,111)
test_mark_knot()_13
test_curveto(138.28,99.0677, 155.539,90.9339, 173.578,86.3634)
test_curveto(191.617,81.7928, 210.435,80.7131, 229,82)
test_mark_knot()_14
test_curveto(266.199,84.5786, 302.694,96.7435, 334,117)

---
testing SpiroCPsToBezier1() using data=path0[].
test_moveto(334,117)_0
test_mark_knot()_0
test_lineto(305,176)
test_mark_knot()_1
test_curveto(278.711,155.106, 245.566,142.988, 212,142)
test_mark_knot()_2
test_curveto(201.253,142.281, 190.34,143.726, 180.596,148.267)
test_curveto(175.724,150.538, 171.185,153.578, 167.419,157.413)
test_curveto(163.652,161.248, 160.67,165.891, 159,171)
test_mark_knot()_3
test_curveto(156.308,179.236, 157.16,188.418, 160.551,196.391)
test_curveto(163.942,204.365, 169.745,211.173, 176.569,216.513)
test_curveto(190.217,227.191, 207.414,231.978, 224,237)
test_mark_knot()_4
test_curveto(250.342,244.977, 276.682,254.316, 299.357,269.917)
test_curveto(322.032,285.518, 340.938,308.152, 347,335)
test_mark_knot()_5
test_curveto(351.153,353.392, 349.045,373.004, 341.926,390.463)
test_curveto(334.806,407.923, 322.8,423.235, 308.112,435.059)
test_curveto(278.737,458.706, 239.7,467.886, 202,467)
test_mark_knot()_6
test_curveto(159.176,465.994, 116.711,452.658, 81,429)
test_mark_knot()_7
test_lineto(114,368)
test_mark_knot()_8
test_curveto(140.313,385.041, 170.105,396.684, 201,402)
test_mark_knot()_9
test_curveto(216.033,404.587, 231.809,405.634, 246.268,400.776)
test_curveto(253.497,398.347, 260.287,394.428, 265.629,388.985)
test_curveto(270.971,383.541, 274.812,376.534, 276,369)
test_mark_knot()_10
test_curveto(277.186,361.482, 275.702,353.665, 272.413,346.801)
test_curveto(269.124,339.938, 264.092,333.997, 258.267,329.099)
test_curveto(246.617,319.303, 232.105,313.721, 218,308)
test_mark_knot()_11
test_curveto(192.746,297.756, 167.726,286.443, 145.32,270.931)
test_curveto(122.913,255.419, 103.089,235.424, 91,211)
test_mark_knot()_12
test_curveto(81.9232,192.662, 77.4184,171.323, 82.5427,151.513)
test_curveto(85.1048,141.608, 90.0758,132.265, 97.2634,124.984)
test_curveto(104.451,117.703, 113.889,112.559, 124,111)
test_mark_knot()_13
test_curveto(137.228,108.96, 150.603,112.998, 163.11,117.762)
test_curveto(175.618,122.526, 188.071,128.128, 201.395,129.403)
test_curveto(208.057,130.041, 214.889,129.554, 221.213,127.364)
test_curveto(227.537,125.174, 233.332,121.213, 237.179,115.737)
test_curveto(241.025,110.261, 242.792,103.23, 241.403,96.6836)
test_curveto(240.708,93.4104, 239.243,90.2939, 237.104,87.7207)
test_curveto(234.965,85.1475, 232.15,83.128, 229,82)
test_mark_knot()_14
test_lineto(0,0)
test_mark_knot()_15
test_lineto(334,117)
testing run_spiro() using data=path1[].
curve 1, line 0, x=80.000000 y=738.000000 t={ bend=0.000000 ch=697.685459 th=-0.287750 l=0.000000 
curve 1, line 1, x=749.000000 y=540.000000 t=o bend=-1.608688 ch=243.813453 th=-1.896438 l=0.000000 
curve 1, line 2, x=671.000000 y=309.000000 t=o bend=-1.770739 ch=173.404152 th=2.616009 l=0.000000 
curve 1, line 3, x=521.000000 y=396.000000 t=o bend=0.937994 ch=157.178243 th=-2.729182 l=0.000000 
curve 1, line 4, x=377.000000 y=333.000000 t=o bend=1.881365 ch=136.029409 th=-0.847817 l=0.000000 
curve 1, line 5, x=467.000000 y=231.000000 t=} bend=1.730337 ch=196.084166 th=2.403091 l=0.000000 
testing spiro_to_bpath() using data from run_spiro(data=path1[],len=6).
test_moveto(80,738)_1
test_mark_knot()_0
test_curveto(172.314,664.426, 287.04,622.654, 403.914,606.06)
test_curveto(462.352,597.763, 521.42,594.908, 580.064,588.226)
test_curveto(638.708,581.543, 698.554,570.643, 749,540)
test_mark_knot()_1
test_curveto(776.321,523.404, 800.431,500.639, 815.305,472.344)
test_curveto(830.178,444.049, 835.075,410.104, 826.082,379.43)
test_curveto(817.089,348.755, 793.992,322.521, 764.639,309.862)
test_curveto(735.286,297.204, 701.137,298.343, 671,309)
test_mark_knot()_2
test_curveto(643.661,318.667, 619.475,335.4, 595.839,352.199)
test_curveto(572.203,368.998, 548.225,386.017, 521,396)
test_mark_knot()_3
test_curveto(493.059,406.245, 461.39,408.353, 433.721,397.395)
test_curveto(419.886,391.915, 407.262,383.305, 397.36,372.199)
test_curveto(387.457,361.092, 380.322,347.504, 377,333)
test_mark_knot()_4
test_curveto(371.073,307.121, 378.034,278.522, 395.545,258.566)
test_curveto(413.056,238.61, 440.626,227.952, 467,231)

---
testing TaggedSpiroCPsToBezier0() using data=path1[].
test_moveto(80,738)_1
test_mark_knot()_0
test_curveto(172.314,664.426, 287.04,622.654, 403.914,606.06)
test_curveto(462.352,597.763, 521.42,594.908, 580.064,588.226)
test_curveto(638.708,581.543, 698.554,570.643, 749,540)
test_mark_knot()_1
test_curveto(776.321,523.404, 800.431,500.639, 815.305,472.344)
test_curveto(830.178,444.049, 835.075,410.104, 826.082,379.43)
test_curveto(817.089,348.755, 793.992,322.521, 764.639,309.862)
test_curveto(735.286,297.204, 701.137,298.343, 671,309)
test_mark_knot()_2
test_curveto(643.661,318.667, 619.475,335.4, 595.839,352.199)
test_curveto(572.203,368.998, 548.225,386.017, 521,396)
test_mark_knot()_3
test_curveto(493.059,406.245, 461.39,408.353, 433.721,397.395)
test_curveto(419.886,391.915, 407.262,383.305, 397.36,372.199)
test_curveto(387.457,361.092, 380.322,347.504, 377,333)
test_mark_knot()_4
test_curveto(371.073,307.121, 378.034,278.522, 395.545,258.566)
test_curveto(413.056,238.61, 440.626,227.952, 467,231)

---
testing SpiroCPsToBezier0() using data=path1[].
test_moveto(80,738)_1
test_mark_knot()_0
test_curveto(172.314,664.426, 287.04,622.654, 403.914,606.06)
test_curveto(462.352,597.763, 521.42,594.908, 580.064,588.226)
test_curveto(638.708,581.543, 698.554,570.643, 749,540)
test_mark_knot()_1
test_curveto(776.321,523.404, 800.431,500.639, 815.305,472.344)
test_curveto(830.178,444.049, 835.075,410.104, 826.082,379.43)
test_curveto(817.089,348.755, 793.992,322.521, 764.639,309.862)
test_curveto(735.286,297.204, 701.137,298.343, 671,309)
test_mark_knot()_2
test_curveto(643.661,318.667, 619.475,335.4, 595.839,352.199)
test_curveto(572.203,368.998, 548.225,386.017, 521,396)
test_mark_knot()_3
test_curveto(493.059,406.245, 461.39,408.353, 433.721,397.395)
test_curveto(419.886,391.915, 407.262,383.305, 397.36,372.199)
test_curveto(387.457,361.092, 380.322,347.504, 377,333)
test_mark_knot()_4
test_curveto(371.073,307.121, 378.034,278.522, 395.545,258.566)
test_curveto(413.056,238.61, 440.626,227.952, 467,231)

---
testing TaggedSpiroCPsToBezier1() using data=path1[].
test_moveto(80,738)_1
test_mark_knot()_0
test_curveto(172.314,664.426, 287.04,622.654, 403.914,606.06)
test_curveto(462.352,597.763, 521.42,594.908, 580.064,588.226)
test_curveto(638.708,581.543, 698.554,570.643, 749,540)
test_mark_knot()_1
test_curveto(776.321,523.404, 800.431,500.639, 815.305,472.344)
test_curveto(830.178,444.049, 835.075,410.104, 826.082,379.43)
test_curveto(817.089,348.755, 793.992,322.521, 764.639,309.862)
test_curveto(735.286,297.204, 701.137,298.343, 671,309)
test_mark_knot()_2
test_curveto(643.661,318.667, 619.475,335.4, 595.839,352.199)
test_curveto(572.203,368.998, 548.225,386.017, 521,396)
test_mark_knot()_3
test_curveto(493.059,406.245, 461.39,408.353, 433.721,397.395)
test_curveto(419.886,391.915, 407.262,383.305, 397.36,372.199)
test_curveto(387.457,361.092, 380.322,347.504, 377,333)
test_mark_knot()_4
test_curveto(371.073,307.121, 378.034,278.522, 395.545,258.566)
test_curveto(413.056,238.61, 440.626,227.952, 467,231)

---
testing SpiroCPsToBezier1() using data=path1[].
test_moveto(80,738)_1
test_mark_knot()_0
test_curveto(172.314,664.426, 287.04,622.654, 403.914,606.06)
test_curveto(462.352,597.763, 521.42,594.908, 580.064,588.226)
test_curveto(638.708,581.543, 698.554,570.643, 749,540)
test_mark_knot()_1
test_curveto(776.321,523.404, 800.431,500.639, 815.305,472.344)
test_curveto(830.178,444.049, 835.075,410.104, 826.082,379.43)
test_curveto(817.089,348.755, 793.992,322.521, 764.639,309.862)
test_curveto(735.286,297.204, 701.137,298.343, 671,309)
test_mark_knot()_2
test_curveto(643.661,318.667, 619.475,335.4, 595.839,352.199)
test_curveto(572.203,368.998, 548.225,386.017, 521,396)
test_mark_knot()_3
test_curveto(493.059,406.245, 461.39,408.353, 433.721,397.395)
test_curveto(419.886,391.915, 407.262,383.305, 397.36,372.199)
test_curveto(387.457,361.092, 380.322,347.504, 377,333)
test_mark_knot()_4
test_curveto(371.073,307.121, 378.034,278.522, 395.545,258.566)
test_curveto(413.056,238.61, 440.626,227.952, 467,231)
testing run_spiro() using data=path2[].
curve 2, line 0, x=233.000000 y=143.000000 t={ bend=0.000000 ch=108.115679 th=-0.046263 l=0.000000 
curve 2, line 1, x=341.000000 y=138.000000 t=o bend=-0.926114 ch=79.881162 th=-0.972377 l=0.000000 
curve 2, line 2, x=386.000000 y=72.000000 t=o bend=1.844174 ch=90.138782 th=0.871797 l=0.000000 
curve 2, line 3, x=444.000000 y=141.000000 t=} bend=0.937994 ch=157.178243 th=-2.729182 l=0.000000 
testing spiro_to_bpath() using data from run_spiro(data=path2[],len=4).
test_moveto(233,143)_1
test_mark_knot()_0
test_curveto(249.592,152.178, 268.758,156.639, 287.698,155.745)
test_curveto(306.638,154.851, 325.283,148.607, 341,138)
test_mark_knot()_1
test_curveto(363.594,122.753, 379.786,98.5395, 386,72)
test_mark_knot()_2
test_curveto(390.58,52.4395, 389.897,31.7133, 384.352,12.4039)
test_curveto(378.808,-6.90551, 368.441,-24.7755, 354.638,-39.3726)
test_curveto(327.032,-68.5668, 285.794,-83.7957, 245.694,-81.2662)
test_curveto(205.595,-78.7367, 167.108,-58.898, 140.822,-28.5107)
test_curveto(114.535,1.87655, 100.493,42.3853, 101.481,82.5524)
test_curveto(102.469,122.719, 118.343,162.269, 145.013,192.321)
test_curveto(171.682,222.373, 208.93,242.787, 248.567,249.365)
test_curveto(288.204,255.942, 329.988,248.669, 365.12,229.172)
test_curveto(400.251,209.674, 428.514,178.075, 444,141)

---
testing TaggedSpiroCPsToBezier0() using data=path2[].
test_moveto(233,143)_1
test_mark_knot()_0
test_curveto(249.592,152.178, 268.758,156.639, 287.698,155.745)
test_curveto(306.638,154.851, 325.283,148.607, 341,138)
test_mark_knot()_1
test_curveto(363.594,122.753, 379.786,98.5395, 386,72)
test_mark_knot()_2
test_curveto(390.58,52.4395, 389.897,31.7133, 384.352,12.4039)
test_curveto(378.808,-6.90551, 368.441,-24.7755, 354.638,-39.3726)
test_curveto(327.032,-68.5668, 285.794,-83.7957, 245.694,-81.2662)
test_curveto(205.595,-78.7367, 167.108,-58.898, 140.822,-28.5107)
test_curveto(114.535,1.87655, 100.493,42.3853, 101.481,82.5524)
test_curveto(102.469,122.719, 118.343,162.269, 145.013,192.321)
test_curveto(171.682,222.373, 208.93,242.787, 248.567,249.365)
test_curveto(288.204,255.942, 329.988,248.669, 365.12,229.172)
test_curveto(400.251,209.674, 428.514,178.075, 444,141)

---
testing SpiroCPsToBezier0() using data=path2[].
test_moveto(233,143)_1
test_mark_knot()_0
test_curveto(249.592,152.178, 268.758,156.639, 287.698,155.745)
test_curveto(306.638,154.851, 325.283,148.607, 341,138)
test_mark_knot()_1
test_curveto(363.594,122.753, 379.786,98.5395, 386,72)
test_mark_knot()_2
test_curveto(390.58,52.4395, 389.897,31.7133, 384.352,12.4039)
test_curveto(378.808,-6.90551, 368.441,-24.7755, 354.638,-39.3726)
test_curveto(327.032,-68.5668, 285.794,-83.7957, 245.694,-81.2662)
test_curveto(205.595,-78.7367, 167.108,-58.898, 140.822,-28.5107)
test_curveto(114.535,1.87655, 100.493,42.3853, 101.481,82.5524)
test_curveto(102.469,122.719, 118.343,162.269, 145.013,192.321)
test_curveto(171.682,222.373, 208.93,242.787, 248.567,249.365)
test_curveto(288.204,255.942, 329.988,248.669, 365.12,229.172)
test_curveto(400.251,209.674, 428.514,178.075, 444,141)

---
testing TaggedSpiroCPsToBezier1() using data=path2[].
test_moveto(233,143)_1
test_mark_knot()_0
test_curveto(249.592,152.178, 268.758,156.639, 287.698,155.745)
test_curveto(306.638,154.851, 325.283,148.607, 341,138)
test_mark_knot()_1
test_curveto(363.594,122.753, 379.786,98.5395, 386,72)
test_mark_knot()_2
test_curveto(390.58,52.4395, 389.897,31.7133, 384.352,12.4039)
test_curveto(378.808,-6.90551, 368.441,-24.7755, 354.638,-39.3726)
test_curveto(327.032,-68.5668, 285.794,-83.7957, 245.694,-81.2662)
test_curveto(205.595,-78.7367, 167.108,-58.898, 140.822,-28.5107)
test_curveto(114.535,1.87655, 100.493,42.3853, 101.481,82.5524)
test_curveto(102.469,122.719, 118.343,162.269, 145.013,192.321)
test_curveto(171.682,222.373, 208.93,242.787, 248.567,249.365)
test_curveto(288.204,255.942, 329.988,248.669, 365.12,229.172)
test_curveto(400.251,209.674, 428.514,178.075, 444,141)

---
testing SpiroCPsToBezier1() using data=path2[].
test_moveto(233,143)_1
test_mark_knot()_0
test_curveto(249.592,152.178, 268.758,156.639, 287.698,155.745)
test_curveto(306.638,154.851, 325.283,148.607, 341,138)
test_mark_knot()_1
test_curveto(363.594,122.753, 379.786,98.5395, 386,72)
test_mark_knot()_2
test_curveto(390.58,52.4395, 389.897,31.7133, 384.352,12.4039)
test_curveto(378.808,-6.90551, 368.441,-24.7755, 354.638,-39.3726)
test_curveto(327.032,-68.5668, 285.794,-83.7957, 245.694,-81.2662)
test_curveto(205.595,-78.7367, 167.108,-58.898, 140.822,-28.5107)
test_curveto(114.535,1.87655, 100.493,42.3853, 101.481,82.5524)
test_curveto(102.469,122.719, 118.343,162.269, 145.013,192.321)
test_curveto(171.682,222.373, 208.93,242.787, 248.567,249.365)
test_curveto(288.204,255.942, 329.988,248.669, 365.12,229.172)
test_curveto(400.251,209.674, 428.514,178.075, 444,141)
testing run_spiro() using data=path3[].
error with run_spiro() using data=path3[].

---
Multi-thread testing of libspiro.
bad pthread_create[167]
time 0.747777

Have ported to Haxe

Hi just letting you know I have ported the code to Haxe so it should now be very cross platform ( haxe supports php7, c++, hashlink ( c and c vm ), neko vm, javascript, flash, c#, java, python, lua ).
https://lib.haxe.org/p/hxSpiro/0.0.1
The demo is still rather rough uses svg javascript but allows you to place points or move them, it should be fairly easy to rebuild for cross target Haxe libraries like NME/Openfl etc..
Feedback welcome, I need to check if enabling some of the commented out inlines would speed up further, but the js test seems fairly responsive. Let me know if a pull request might be suitable and what form it might take, but seems fine for it to be separate, but thought it might interest you.

Output Bézier splines not deterministic enough

Hello @JoesCat, good day.

A problem I've been thinking about for a while is how we can increase the determinism† of the output Béziers from libspiro, at the cost of increased path complexity, and, perhaps, even some mathematical accuracy.

This would make it much easier for Spiro splines to be used in variable fonts, or in other cases where we'd want interpolation. Right now, Spiro users such as me usually have to redraw one or both splines to create an interpolation-compatible spline.

It's clearly quite the challenge. Before I give you my thoughts, what are yours?

† The type of determinism I'm specifically referring to is determinism in the number of points. So, for two Spiro splines with n points, assuming all point types are equal, and both splines have the same openness/closedness, we always get back a Bézier spline with N points, no matter the configuration of the Spiro points.

Segmentation fault in call-test20

Hi,

The issue can be reproduced out of the box when building and running the test suite on OpenBSD and FreeBSD.

It can be reproduced on Linux with GCC by enabling the Stack Smashing Protector (SSP):

./configure CFLAGS=-fstack-protector
make
tests/call-test20

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.