Sign in to follow this  
VanillaSnake21

C++ How to correctly scale a set of bezier curves?

Recommended Posts

VanillaSnake21    175

I've got a working implementation of 4d and 1d bezier curve font generator, however I'm not sure how to now transition into actually making text. As of right now I create my font by clicking and dragging control vertices on the screen, once I have a few curves aligned I designate it as a letter and save the font. But I'm not sure what coordinate system to use to make sure that I can scale the existing curves to any size? I'm thinking to have the letter sit in like a canonical box with -1 to 1 in both x and y, but then how do I re normalize the curves and still have the ability to plot points directly on screen? As of right now the control vertices are in viewport space of [0 to screen dimention], so when I plot a point I just take the client mouse coordinates. But if I choose to project the final letter to -1 to 1 space, I can only do so once I draw all the curves for that letter as I need the bounding box of all the curves. So what is the right way to approach this? 

 

This is probably a bit convoluted, the point of the question is how do I transition from font editor to actual font. Do I have to unproject the curves when I open them in font editor and duplicate as working copies and only bake the final normalized letter into the font when I'm done editing it or else how would I do it at runtime?

Edited by VanillaSnake21

Share this post


Link to post
Share on other sites
JoeJ    2567

I'm not sure what you ask for, but in any case you seem to reinvent things that have common standarts e.g. TrueType and Postscript, sou you should find some answers by looking at their specs.

I wonder why you don't use those standarts, as there are font editors and also libs to render those fonts with graphics APIs, probably all for free.

One main problem is the horizontal distance between letters. AFAIK this is solved by default left/right bounds, but there are also individual settings for custom pairs of letter, e.g. "LA" should be as close together as possible, while "IL" should have some space between. (I know this from bitmap fonts typical for games like http://www.angelcode.com/products/bmfont/ , probably vector fonts do the same)

Share this post


Link to post
Share on other sites
VanillaSnake21    175

It's my own framework, I'm not willing to use anything but the most low level libraries, as I'm not even using a graphics api. My question was how to represent the spline correctly internally so it could both be used in letter glyphs as well as modified in the editor. I've settled on having a duplicate structure at this point, I have one representation for a spline when I'm dragging around it's vertices in the editor and another normalized representation for when it's rendered, I was just looking for a single elegant implementation in this question. 

Share this post


Link to post
Share on other sites
VanillaSnake21    175
57 minutes ago, ApochPiQ said:

Why can't your editor just use normalized coordinates, too? After any change to the spline's control points, compute the correct normalization factor to return everything to the unit square, and apply it/redraw.

 

Because I can't normalize until I get the final shape of the letter, lets say letter A. Takes 3 curves, / -- \  , if I just renormalize after I add the second curve, the structure shifts to renormalized units, meaning it shifts to the center of the canonical box as I have it now, so I have to manually renormalize once I finalize the letter. That's how I have it now, it's a bit tedious and I was looking for a way to maybe use alternate coordinate systems to have a more elegant implementation, but not every piece of code has to be perfect I guess, just have to settle on this for now.

Share this post


Link to post
Share on other sites
Alberth    9508
7 hours ago, VanillaSnake21 said:

It's my own framework, I'm not willing to use anything but the most low level libraries

JoeJ was not saying to adopt the standards or adopt libraries that implement the standard, he was suggesting to just look at how they solved the same problem as a source of inspiration.

Share this post


Link to post
Share on other sites
VanillaSnake21    175
9 hours ago, Alberth said:

JoeJ was not saying to adopt the standards or adopt libraries that implement the standard, he was suggesting to just look at how they solved the same problem as a source of inspiration.

 I mean suggesting to dig through a mature open source library code to see how it does a certain specific action is a bit of an overkill imo. If there are some docs you can point me to that deal with this issue, that's another thing.

Share this post


Link to post
Share on other sites
JoeJ    2567

Maybe looking at example rendering implementations linked at the angelcode webpage helps. (Probably all OpenGL, but it would be the same for a software renderer.) The data per letter is pretty self explaining.

But i don't know if this would help with your problem - i still have no idea what the problem is exactly. Probably it's the same with others. I think you should provide screenshots from your tool and / or some illustration to visualize your problem.

Share this post


Link to post
Share on other sites
VanillaSnake21    175
7 hours ago, JoeJ said:

Maybe looking at example rendering implementations linked at the angelcode webpage helps. (Probably all OpenGL, but it would be the same for a software renderer.) The data per letter is pretty self explaining.

But i don't know if this would help with your problem - i still have no idea what the problem is exactly. Probably it's the same with others. I think you should provide screenshots from your tool and / or some illustration to visualize your problem.

 

It's not easy to explain or even draw or demonstrate, I'll try again but bear with me. When I'm making a font in my editor I do so by plotting single control vertices. For every click I create a single control vertex, after I plot 4 of them it makes a 4d bezier curve. Now how do I store the actual positions of the control vertex? Right now I just have them as exact mouse coordinates where I clicked on the screen so CV1(100px, 100px) CV2( 300px, 300px) and so on until CV4 which now makes a curve. These are all in screen space. Now I add a few more curve lets say which form a letter, so all these curves are being manipulated in pixel coordinates. So now if I want to actually use these letters and scale them to any font size, I can't use these screen coordinates anymore, I have to fit the letter in some scalable space like 0 to 1. So I have to convert all the vertex coordinates into that space. Right now I'm doing that manually, I just have a button in my editor called Normalize, so once I'm happy with the letter I've formed, I click normalize and it transforms all the vertices into normalized 0 to 1 space. My question was whether I can avoid doing the normalization manually and work in some space that is normalized from the get go. As in when I plot the point with the mouse, I wouldn't store the location of the mouse as the vertex coordinate, but right away transoform the mouse coordinate into a normalized space. I hope that clears up what my intentions with the question were. It's not really a problem as everything works just fine as of now, I just wanted to know if there is a more elegant way of doing this.

Share this post


Link to post
Share on other sites
ApochPiQ    23000

There can be no magic solution.

You want arbitrary sized splines that are normalized. No math can predict how big you will make your splines; to normalize you must by definition know how big the spline is.

The compromise is to draw boundaries ahead of time and force all characters in your font to adhere to predefined dimensions. If you know a glyph's size ahead of time you can draw its splines in normal space.

But you can't have arbitrary glyph dimensions and also eliminate the need for explicit normalization steps.

Share this post


Link to post
Share on other sites
JoeJ    2567

Yeah, but the bounding rect (or normalization square - however we call it) needs to be part of the editing process anyways. To be more clear, the user needs to specify left and right bounds an the base line manually. The baseline ensures all letters like 'mn' have the same bottom height. A letter like 'g' is lower than the baseline and a algorithm can't get the base just from calculating bounds. A letter like 'o' is a tiny bit lower (and higher) than 'nm' too, so it does not appear smaller than other letters to the eye. The user should set those bounds and he should be able to tweak them later until he is happy with the result. So it's like with any other form of content creation: You keep the original data and convert it to game asset anytime the artist does any changes.

Share this post


Link to post
Share on other sites
VanillaSnake21    175

Oh so I should contain all the curves in a region and just map it from 0 to 1. Right, that makes sense. I guess come to think of it every font editor has set size glyphs, not sure why I thought I needed arbitrary sizes inside the editor. Thanks. Also @JoeJ, I didn't consider the baseline and letter metrics, thanks.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

  • Similar Content

    • By hyyou
      I have recently read T-machine's ECS data structure. (link)
      Its "best" version (4th iteration) can be summarized into a single image (copy from T-machine's web) :-

      This approach also appears in a VDO https://www.youtube.com/watch?v=NTWSeQtHZ9M  , with slide and source code ( https://github.com/CppCon/CppCon2015/tree/master/Tutorials/Implementation of a component-based entity system in modern C++ ) .
      There are many people (including me) believe that the above approach lacks data locality, because Position-1,Position-2,Position-3, ... are not stored in a contiguous array.
      However, many attempts failed to elegantly make it contiguous.  (e.g. stackoverflow's link) 
      I also realized that, to make it contiguous, the cost of query Entity<-->Component is unavoidably significantly higher. (I profiled) I would like to hear that ....
      Is such mega-array (T-machine's 4th) generally better (performance-wise) than storing each type component to its own contiguous array?  (like http://www.randygaul.net/2013/05/20/component-based-engine-design/ )  ? My references (those links) are quite old.   Now, are there any others better approaches? (If you happen to know) I know that its depend on data access pattern (i.e. cache miss).  I still want to hear more opinion/argument about it, because I may need to rewrite a large part of my engine to make it support 30,000+ entity (personal greed).    
      Thank.
       
       
    • By Bleys
      There's a C++ library I'm developing and while it's not specifically targeted at games, all projects that I know of which use it are games.

      It's called DynaMix. In short it allows you to compose and modify polymorphic objects at run time. This has proven to be rather useful in gameplay programming. Compared to more traditional ways to write gameplay, like scripting, it has some benefits (well, and some drawbacks).
      It's C++ so it usually is at least a bit faster (and in the cases that I know of a lot faster) and less power consuming than scripts You can reduce code complexity when you don't have a C++<->scripting language binding layer. You can reuse utility code between the core and gameplay subsystems (instead of having to rewrite it in the scripting language. Hotswapping is supported relatively easily achievable Still, it's C++ so I guess it's a bit harder to write, and impossible to delegate to game-designers and other non-programmers Because of this it has found a niche of sorts in mobile games, where the benefits from the performance and smaller power consumption outweigh the fact that the gameplay code is strictly programmer country (whereas desktop/console developers, might be less willing to pay this price)

      The repository is here: https://github.com/iboB/dynamix
      The docs are here: https://ibob.github.io/dynamix/

      I have written about it before back when it used to be called Boost.Mixin. I have since rebranded it and removed the dependency on Boost. Recently I released a new version and I'm using this as an opportunity to gather more feedback and, perhaps, maybe new users.

      So, any comments and questions are welcome
    • By VanillaSnake21
      I've restructured some of my code to use namespaces and started getting problems in a module that was working correctly previously. The one in question is a DebugWindow, what happens is I give it a pointer to a variable that I want to monitor/change and it's job is to display that variable in a separate window along with a some + and - buttons to in/decrement the variable.
      These are the relevant portions:
       
      WindowManager.h
      namespace WindowManager { /* WindowManager functions snipped */ namespace DebugWindow { void AddView(double* vard, std::wstring desc, double increment); void AddView(std::wstring* vars, std::wstring desc); void CreateDebugWindow(int width, int height, int x, int y); } }  
       
      Application.cpp is the main app, it calls the above functions to set the watch on the variables I need to see in real-time
      void ApplicationInitialization() { //create the main window UINT windowID = SR::WindowManager::CreateNewWindow(LocalWindowsSettings); //initialize the rasterizer InitializeSoftwareRasterizer(SR::WindowManager::GetWindow(windowID)); //create the debug window SR::WindowManager::DebugWindow::CreateDebugWindow(400, LocalWindowsSettings.clientHeight, LocalWindowsSettings.clientPosition.x + LocalWindowsSettings.clientWidth, LocalWindowsSettings.clientPosition.y); //display some debug info SR::WindowManager::DebugWindow::AddView((double*)&gMouseX,TEXT("Mouse X"), 1); SR::WindowManager::DebugWindow::AddView((double*)&gMouseY, TEXT("Mouse Y"), 1); }  
      The variables gMouseX and Y are globals in my application, they are updated inside the App's WindProc inside the WM_MOUSEMOVE like so :
      case WM_MOUSEMOVE: { gMouseX = GET_X_LPARAM(lParam); gMouseY = GET_Y_LPARAM(lParam); /* .... */ }break;  
       
      Now inside the AddView() function that I'm calling to set the watch on the variable
      void AddView(double* vard, std::wstring desc, double increment) { _var v; v.vard = vard; // used when variable is a number v.vars = nullptr; // used when varialbe is a string (in this case it's not) v.desc = desc; v.increment = increment; mAddVariable(v); }  
      _var is just a structure I use to pass the variable definition and annotation inside the module, it's defined as such
      struct _var { double* vard; //use when variable is a number double increment; //value to increment/decrement in live-view std::wstring* vars; //use when variable is a string std::wstring desc; //description to be displayed next to the variable int minusControlID; int plusControlID; HWND viewControlEdit; //WinAPI windows associated with the display, TextEdit, and two buttons (P) for plus and (M) for minus. HWND viewControlBtnM; HWND viewControlBtnP; };  
      So after I call AddView it formats this structure and passes it on to mAddVariable(_var), here it is:
      void mAddVariable(_var variable) { //destroy and recreate a timer KillTimer(mDebugOutWindow, 1); SetTimer(mDebugOutWindow, 1, 10, (TIMERPROC)NULL); //convert the variable into readable string if it's a number std::wstring varString; if (variable.vard) varString = std::to_wstring(*variable.vard); else varString = *variable.vars; //create all the controls variable.viewControlEdit = CreateWindow(/*...*/); //text field control variable.minusControlID = (mVariables.size() - 1) * 2 + 1; variable.viewControlBtnM = CreateWindow(/*...*/); //minus button control variable.plusControlID = (mVariables.size() - 1) * 2 + 2; variable.viewControlBtnP = CreateWindow(/*...*/); //plus button control mVariables.push_back(variable); }  
      I then update the variable using a timer inside the DebugWindow msgproc
      case WM_TIMER: { switch (wParam) { case 1: // 1 is the id of the timer { for (_var v : mVariables) { SetWindowText(v.viewControlEdit, std::to_wstring(*v.vard).c_str()); } }break; default: break; } }; break;  
      When I examine the mVariables, their vard* is something like 1.48237482E-33#DEN. Why does this happen?
       
      Also to note is that I'm programming in C like fashion, without using any objects at all. The module consists of .h and .cpp file, whatever I expose in .h is public, if a function is only exposed in .cpp it's private . So even though I precede some functions with m_Function it's not in a member of a class but just means that it's not exposed in the header file, so it's only visible within this module.
       
      Thanks.
       
       
    • By khawk
      GameDev.net member @Bleys has released a C++ library that may be useful for game developers.

      Called DynaMix, the library:
      You can access the repository at https://github.com/iboB/dynamix and documentation at https://ibob.github.io/dynamix/.
      Learn more from the thread:
      .

      View full story
    • By khawk
      GameDev.net member @Bleys has released a C++ library that may be useful for game developers.

      Called DynaMix, the library:
      You can access the repository at https://github.com/iboB/dynamix and documentation at https://ibob.github.io/dynamix/.
      Learn more from the thread:
      .
  • Popular Now