[C++] Struktura dla wzoru matematycznego

0

Witam,

MIMO IŻ TO DŁUGI POST TO PROSZĘ O ZAINTERESOWANIE SIĘ NIM!

Piszę program w którym będzie można pisać wzory matematyczne. Problem mam z napisaniem struktury. Przyznam, że już długo nad tym siedzę, już rysowałem wzory, ale ciągle jest problem z rozmieszczeniem elementów matematycznych w przypadku złożonych wzorów.

Struktura przechowująca wzór to drzewo, ale nie binarne. Każdy element (prócz korzenia) posiada wskaźnik "Parent" który wskazuje na rodzica i każdy element posiada obiekt Child typu std::vector przechowujący wskaźniki na elementy potomne (jeżeli takie są).

W super uproszczeniu podaje jak mniej więcej wyglądają 2 klasy.

expression.h

class Expression
{
	public:
		Expression();
		~Expression();
		
		//dodaje element do struktury (wstawia go na koncu
		virtual Expression* Add(Expression*) = 0; 
		//dodaje element potomny
		virtual Expression* AddChild(Expression*) = 0;
		
		Expression* Head; //pierwszy element
		Expression* Parent; //rodzic
		std::vector<Expression*> Child; //dzieci
		
		//pokazuje elementy od podanego w parametrze
		//jezeli podamy w nim Head to odrysuje wszytko
		//jest to rekurencyjne wywolanie metody Show()
		void Show(Expression*);
	
	protected:
		//odrydowuje element danego typu
		virtual void Show() = 0;
		//metoda przesuwa wszystkie elementy potomne
		//1 paremetr: ile w lewo
		//2 parametr: ile w gore
		virtual void Move(Expression*,const int, const int);
};

subscript.h

class Subscript : public Expression
{
	public:
		Expression();
		~Expression();
		
		virtual Expression* Add(Expression*); 
		virtual Expression* AddChild(Expression*);
		
		Expression* Head; 
		Expression* Parent; 
		std::vector<Expression*> Child; 
		
		void Show(Expression*);
	
	protected:
		virtual void Show();
		virtual void Move(Expression*,const int, const int);
};

Nagłówki klas które napisałem to oczywiście chyba największe uproszczenie tego co mam, ale chodzi o coś innego. Czy taka koncepcja jest dobra.

Wywołanie wyglądałby tak:

//to w jakims konstruktorze
//expression to skladowa prywatna jakies klasy programu
expression = new Begin; 
//Begin to taka klasa, ktorej obiekt moze wystapic tylko raz
//jest pierwszym elementem kazdego wzoru matematycznego

//klikniecie na jakis przycisk
Identifier* id = new Identifier; //reprezentuje identyfikator
id->Ascii(242);
id->Font->Name("Math1");
//znak 242 w czcionce Math1 to znak calki

expression = expression->Add(id);

I TERAZ:
Opisałem jak podkreślam mniej więcej to wyglądam ze zdecydowanie na mniej, ale rzecz w tym że mam problem z rozmieszczeniem poszczególnych elementów.

Kiedy umieszczam pierwszy element (np. podaną wyżej całkę) sprawdzany jest jej rozmiar. Ponieważ jest pierwszym elementem umieszczany jest w punkcie 0,0. Następny element jest umieszczany w punkcie:
Poprzedni->Left + Poprzedni->Szerokosc
Jeżeli dodawany element jest wyższy od poprzedniego to poprzedni jest odbiżany metodą Move;

Metoda ta nie sprawdza się dla zagnieżdżonych elementów.
Dlaczego?
Dodawane elementy są dodawane do pierwszego elementu zwanego rodzicem i zarazem korzeniem. Dodaje coś do niego i mogę zmienić to w rodzicu czyli jednocześnie w korzeniu.

Przy bardziej zagłębionych elementach musiałbym się odwoływać do rodzica rodzica rodzica... aż do korzenia. Teoretycznie wydaje się to proste, ale przy implementowaniu metody Add() w klasie Subscript (indek dolny) może to już być zagmatwane. Trzeba tak napisać żeby każdy potomek zmieniał wielkość rodzica. O ile? Zaczynają się schody.

WIEM ŻE NAPISAŁEM DUŻO I BYĆ MOŻE NIEZROZUMIALE ALE NIE WIEM JAK OPISAĆ TO PROŚCIE!

Chodzi mi o to abyście mi podpowiedzieli jakiś schemat do obliczania położenia poszczególnych elementów.

Z góry dzieki za pomoc.
Jeśli w ogóle ktoś to doczytał:-)

0

nie wiem czy dobrze zrozumiałem, ale ty chcesz wygenerować obraz tekstowy wzoru (w linii) na podstawie danych w strukturze wzoru?

0

Chcę zrobić taki edytor (lub przynajmniej viewer) jaki jest w Wordzie do pisania wzorów matematycznych.

Dokładnie to ma to być taki interpreter języka MathML. MathML jest oparty na XMLu.
Chodzi o prostą wersję programu Formulator MathML Weaver
Link: http://www.mmlsoft.com/index.php?option=com_content&task=view&id=11&Itemid=12

Jeżeli struktura będzie oparta na takim drzewie to nie będzie problemu z wczytaniem z XML-a. Z resztą nie o to mi chodzi. Mam strukturę, ale rysuje mi jakieś podstawowe wzorki, ale przy bardziej złożonych jest problem z położeniem bo źle zapisuje położenie.

Jeżeli ktoś mógłby zobaczyć jak ten program wygląda to pewnie będzie wiedział o co chodzi.

0

Aaaaaaaaaa
Dokładnie to piszę to w C++ Builder. Ustawiłem sobie PainBox-a i na jego Canvasie wszystko rysuję.

Jeżeli chcę odrysować znak całki to sprawdzam w czcionce Math jaki ma znak ASCII konwertuje na Unicode i metodami dostępnymi w klasie TCanvas sprawdzam wysokość i szerokość tego znaku.
Wstawiam taki obiekt w odpowiednie miejsce w strukturze i rysuje tylko............
ZE JEST PROBLEM Z ROZMIESZCZENIEM. Proszę o pomoc w zorganizowaniu struktury tak aby metody Left() i Top() działały poprawnie.

0

To może inaczej...
Postanowiliście zrobić taki program, w którym będziecie mogli pisać wzorki matematyczne. Jak według Was powinna wyglądać struktura i w jaki sposób... zamierzacie obliczać położenie elementów wzoru??

0

To zaliczenie?? Bo jak nie to weź OOo i tam masz zaawansowany edytor MathML

0
winerfresh napisał(a)

To zaliczenie?? Bo jak nie to weź OOo i tam masz zaawansowany edytor MathML

No niestety zaliczenie. W OOo edytor troszkę inaczej wygląda. Właśnie rozpocząłem kolejne podejście. Tym razem troszkę inna struktura, ale na razie nad nią myślę. Jeżeli możecie mi cokolwiek pomóc, chociażby jakieś sugestie to będę bardzo wdzięczny.

0

hmm ja bym kombinowal cos z ONP i drzewem ktorego wezlami byly by proste wyrazenia w nawiasach
ale ogolnie nie zastanawialem sie jak cos takiego zrobic ;)

0

ONP to do liczenia. Problem jest z reprezentacją graficzną.
Problem z pozycjonowaniem rozwiązałbym w ten sposób: rodzic pyta każde swoje dziecko jaki ma rozmiar i na podstawie tego co otrzyma ustala pozycję względną dzieci oraz swój rozmiar. Rekurencyjnie wywołując tą procedurę od elementu nadrzędnego wypozycjonujesz wszystkie elementy, po tym etapie zostanie już tylko zamiana pozycji względnych na bezwzględne. Sposób pozycjonowania będzie różny dla różnych operatorów.

Np. operator dodawania będzie miał dwoje dzieci. Przypuśćmy, że jedno dziecko ma rozmiar W1 na H1, drugie W2 na H2. Znak sumy ma wielkość WS na HS. Zatem szerokość całego elementu sumy to W1+WS+W2, a wysokość to Max(H1, HS, H2). Dalej wyliczasz względną pozycję składników tak, aby były ułożone "równo", czyli pierwszy składnik do lewej wyśrodkowany w pionie, drugi składnik do prawej też wyśrodkowany w pionie.
Na końcu wyjdzie ci, że całe wyrażenie ma rozmiar W na H. Znowu rekurencyjnie ustalasz już bezwzględną pozycję każdego elementu na podstawie wyliczonych rozmiarów i pozycji względnych, zaczynając od pozycji 0,0 dla elementu głównego.

0
adf88 napisał(a)

ONP to do liczenia. Problem jest z reprezentacją graficzną.
Problem z pozycjonowaniem rozwiązałbym w ten sposób: rodzic pyta każde swoje dziecko jaki ma rozmiar i na podstawie tego co otrzyma ustala pozycję względną dzieci oraz swój rozmiar. Rekurencyjnie wywołując tą procedurę od elementu nadrzędnego wypozycjonujesz wszystkie elementy, po tym etapie zostanie już tylko zamiana pozycji względnych na bezwzględne. Sposób pozycjonowania będzie różny dla różnych operatorów.

Np. operator dodawania będzie miał dwoje dzieci. Przypuśćmy, że jedno dziecko ma rozmiar W1 na H1, drugie W2 na H2. Znak sumy ma wielkość WS na HS. Zatem szerokość całego elementu sumy to W1+WS+W2, a wysokość to Max(H1, HS, H2). Dalej wyliczasz względną pozycję składników tak, aby były ułożone "równo", czyli pierwszy składnik do lewej wyśrodkowany w pionie, drugi składnik do prawej też wyśrodkowany w pionie.
Na końcu wyjdzie ci, że całe wyrażenie ma rozmiar W na H. Znowu rekurencyjnie ustalasz już bezwzględną pozycję każdego elementu na podstawie wyliczonych rozmiarów i pozycji względnych, zaczynając od pozycji 0,0 dla elementu głównego.

A co to jest ONP? Bo niestety nie wiem?

Pisałem, że zacząłem od początku i robię teraz to dokładnie tak jak opisałeś. Tylko co do tych pozycji:
Jak dodaję nowy element, np. ułamek do np. znaku równości, to trzeba obniżyć poprzedni element. Wielkości elementów już bezproblemowo rekurencyjnie program oblicza, ale nie rozumiem jak rekurencyjnie obliczać położenie. Jak zacznę od punku 0,0 to położenie następnego elementu będzie równe poprzedni->left + poprzedni->width.
jeżeli dodawany->height > poprzedni-height to trzeba przejść po całym drzewie i obniżyć o odpowiednią liczbę położenie w pionie?? Ale czy każdy element drzewa musi być obniżony?? W każdym przypadku???

0

Odwrotna Notacja Polska (tzw. postfiksowa).

0
winerfresh napisał(a)

Odwrotna Notacja Polska (tzw. postfiksowa).

Nie rozpoznałem skrótu :-)
Tak się zastanawiałem i wydaje mi się że chyba raczej nie trzeba do liczenia położenia wykorzystywać ONP.
Gratulujemy reprezentacji siatkarzy Mistrzostwa Europy!!!

0

Mniej więcej tak to widze

struct Size
{
   int width;
   int height;
};

struct Position
{
   int left;
   int top;

   friend Position operator+(const Position &pos, const Size &size)
   {
      Position ret = pos;
      ret.left += size.width;
      ret.top += size.height;
      return ret;
   }
};

class Placeholder;
class Root;

class Expression
{
friend class Placeholder;
friend class Root;

protected:
   std::vector<Expression*> children;
   typedef std::vector<Expression*>::iterator child_iterator;
   Expression* parent;

   Size absolute_pos;
   Position pos;
   Size size;

   void AddChild(Expression* child)
   {
      child->parent = this;
      children.push_back(child);
   }

   virtual ~Expression()
   {
      for (child_iterator child = children.begin(); child != children.end(); child++)
         delete *child;
   }

   //oblicz względne pozycje swoich dzieci oraz własny rozmiar
   //zwróć true, jeśli rozmiar się zmienił
   virtual bool Layout() = 0;

   //rekurencyjnie liczy pozycje bezwzględne właną oraz wszystkich dzieci, na podstawie pozycji względnych
   void Move()
   {
      pos = parent->pos + absolute_pos;
      for (child_iterator child = children.begin(); child != children.end(); child++)
         (*child)->Move();
   }

   static void SetAbsPos(Expression *expression, int left, int top)
   {
      expression->absolute_pos.width = left;
      expression->absolute_pos.height = top;
   }

   //oblicza rekurencyjnie rozmiary i względne pozycje wszystkich dzieci, oraz rozmiar własny
   void LayoutAll()
   {
      for (child_iterator child = children.begin(); child != children.end(); child++)
         (*child)->Layout();

      Layout();
   }

   //zminił się rozmiar jednego z dzieci, pozycjonujemy elementy, które tego wymagają
   void ChildChanged()
   {
      if (Layout()) //liczmy nowe pozycje względne dzieci oraz swój rozmiar
         parent->ChildChanged(); //jeśli nasz rozmiar się zmienił powiadamy rodzica
      else
         Move(); //w przeciwnym razie liczymy nowe, bezwzględne pozycje wszystkich dzieci
   }

   //rysuje grafikę danego elementu
   virtual void DrawYourself() = 0;

public:
   //rysuje siebie i wszystkie swoje dzieci
   void Draw()
   {
      for (child_iterator child = children.begin(); child != children.end(); child++)
         (*child)->Draw();

      DrawYourself();
   }

   unsigned Width() { return size.width; }
   unsigned Height() { return size.height; }

   //wstawia siebie w miejsce innego wyrażenia
   void ReplaceWith(Expression *expression)
   {
      *find(expression->parent->children.begin(), expression->parent->children.end(), expression) = this;
      expression->parent = parent;
      delete this;
   }

};

//"puste" wyrażenie, miejsce do zastąpienia przez inne wyrażenia
class Placeholder : public Expression
{
protected:
   virtual bool Layout() { return false; }
   void DrawYourself() { /* rysowanie jakiejś ramki ... */ }

public:
   Placeholder() { size.width = 10; size.height = 20; }
};

//korzeń
class Root : public Expression
{
protected:
   virtual bool Layout() { return false; }
   void DrawYourself() { }

public:
   Root()
   {
      AddChild(new Placeholder());
   }

   Expression* RootExpression() { return children[0];  }

   //pozycjonuje całe wyrażenie
   void LayoutExpression()
   {
      LayoutAll();
      RootExpression()->Move();
   }
};

//przykładowo ułamek
class Frac : public Expression
{
protected:
   virtual bool Layout()
   {
      Size old_size = size;

      size.width = max(Numerator()->Width(), Denominator()->Height()) + 4; //szerokość szerszego dziecka + mały margines
      size.height = Numerator()->Width() + Denominator()->Height() + 8; //wysokość obu dzieci + mały margines
      SetAbsPos(Numerator(), (size.width - Numerator()->Width() - 4) / 2, 0); //pozycja licznika w gorze na srodku
      SetAbsPos(Denominator(), (size.width - Denominator()->Width() - 4) / 2, size.height - Denominator()->Height()); //pozycja mianownika w dole na srodku

      return old_size != size;
   }

   virtual void DrawYourself() { /* rysuj kreskę ułamkową */ }

public:
   Frac()
   {
      AddChild(new Placeholder()); //licznik
      AddChild(new Placeholder()); //mianownik
   }

   Expression *Numerator() { return children[0]; }
   Expression *Denominator() { return children[1]; }
};

//...
Root root;
//przykładowo dodanie ułamka "1/2"
Frac *frac = new Frac;
root.RootExpression()->ReplaceWith(frac);
frac->Numerator()->ReplaceWith(new Number(1));
frac->Denominator()->ReplaceWith(new Number(2));

root.LayoutAll(); //wypozycjonuj wszystko
root.Draw(); //narysuj wszystko

0

WOW!!!
Pisząc tego posta nie spodziewałem się takiej odpowiedzi.
Wielki szacunek i wielkie dzięki.
Zaraz analizuję.

Thx raz jeszcze.

0

Do adf88:

Zacząłem trochę po swojemu kombinować, trochę tak jak Ty mi napisałeś, ale w przypadku gdzie wstawiasz ułamek jak zamiast licznika wstawisz kolejny ułamek to nie będzie lipy??
W przypadku gdy to będzie pierwsze wyrażenie to ok, ale gdy:

  • najpierw mamy ciąg wyrażeń, np liczb, gdzie ich wysokość to np. 40
  • ich położenie od góry to 0
  • teraz wstawiamy ułamek
  • wyrażeni przesuwa się tak aby kreska ułamkowa była po środku wysokości, czyli jeżeli mamy 12345 i dodajemy ułamek 1/2 to położenie elementu '12345' od góry = 20, a ułamka = 0 (nie liczę odstępów i miejsca na kreskę ułamkową - żeby łatwiej było)

A teraz: jeżeli w ułamku zamiast licznika wstawiamy kolejny ułamek, np.3/4 to wysokość całego ułamka równa jest 120. Wyrażenie '12345' zatem ustawi się w połowie JUŻ NIE KRESKI TYLKO GŁÓWNEGO UŁAMKA, czyli pozycja od góry to 40.

Tak będzie czy się mylę?

0

Zgadza się, niektóre rzeczy trzeba jeszcze dopracować.
W tym przypadku wymagamy aby kreska ułamkowa znajdowała się w połowie wiersza.
Właśnie - wiersz to też powinna być klasa dziedzicząca po Expression, będzie po prostu zbiorem dowolnej¹ ilości innych wyrażeń układanych w jednej linii.
Zatem rozwiązaniem byłoby:

  • dodać do ułamka metodę zwracającą położenie jego kreski ułamkowej
  • w metodzie Layout klasy wiersza dodać specjalny warunek dla ułamków (można z dynamic_cast skorzystać, aby ułamki wykrywać)
    Być może znajdą się jeszcze inne takie rzeczy związane z pozycją i będzie się dało jakoś ten mechanizm zunifikować.

Podobna sprawa jest z wielkością, czy raczej skalą. Wykładnik potęgi czy granice całkowania będą musiały być zmniejszone. Tudzież pytając dzieci o rozmiar (metoda Layout) trzeba by im podać w jakiej skali je chcemy namalować. Ale nie można zmniejszać ich w nieskończoność, każdy element będzie miał swoje minimalne rozmiary. Wtedy przykładowo potęgowanie:
xxxxxxxxxxxxx
zapisane normalnie jak w matematyce nie "zniknie" przy którymś wykładniku.

Zacząłem trochę po swojemu kombinować, trochę tak jak Ty mi napisałeś
No i super. Ja chciałem głównie koncepcję pokazać i doszedłem do wniosku, że kodem będzie najlepiej. Tylko jak będziesz kontynuować dyskusję w tym wątku, to najlepiej przedstawiaj aktualny kod, możesz go zamieszczać tutaj:
http://pastebin.4programmers.net

¹ do upierdliwych - oczywiście ilość jest ograniczona możliwościami sprzętu i OS.

0

Właściwie to pracuje jeszcze nad położeniem elementów (metody Left i Top), ale to chyba właśnie przez próbę zrobienia tego od razu w poprawny sposób. Wielkości elementów zwraca mi dobrze. Na pewno dziś wrzucę jakiś kod.

Dzięki za pomoc. Proszę jeszcze tu zajrzeć :-)

0

Właśnie wpadłem na to jak zunifikować problemu z ułamkiem. Otóż każdy element powinien zwracać pewną metodą swoją pozycję względem linii bazowej (baseline), linii która przebiega nieco niżej środka wiersza. Np. literka "a" ma linie bazową na samym dole, natomiast literka "p" ma linie bazową na dole brzuszka, ogonek wystaje pod linie bazową.

Punktem odniesienie nie koniecznie musi być linia bazowa. Chyba linia środka wiersza będzie odpowiedniejsza. Można też użyć obydwu, element sam wybierze w jaki sposób chce określać swoją pozycję, tak chyba będzie najlepiej.

0

Kurde, adf88 czytam te Twoje posty w tym temacie i innych i Ty jednak masz łeb. Gratuluje i dziękuję!

I takim elementem baseline np. w ułamku będzie BOTTOM licznika lub TOP mianownika, a dla całki elementem baseline będzie pierwsze dziecko.

Dla ułamka:

mfrac
    |
    -> 1
    |
    -> 2

Dla całki:

munderover
    |
    -> &int; //znak całki w języku MathML
    |
    -> 1 //dolna granica
    |
    -> 2 //górna granica

Dobrze myślę? Tylko teraz jak rozpoznawać, że w tym przypadku baseline to położenie a w tym to kombinacja położenia z wysokością? Zapewne jakaś wirtualna metoda:

virtual const int Baseline() = 0;
0

Chyba właśnie tak. Jeżeli będzie wirtualna metoda, która będzie wiedziała, które dziecko odpowiada za baseline, to będzie można się odwołać do właściwego dziecka lub od razu zwrócić dane o położeniu czy wielkości.

0
gosc_z_pytaniem napisał(a)

I takim elementem baseline np. w ułamku będzie BOTTOM licznika lub TOP mianownika
Dlaczego tak dziwnie? Ułamek ma mieć po prostu kreskę na środku. Aby tak wypozycjonować ułamek trzeba by obliczyć baseline na podstawie wysokości wiersza. Można skorzystać tutaj ze skali o której pisałem wcześniej. Przypuśćmy, że dla wiersza w skali 1 odległość środka wiersza do baseline jest równa B (stała liczba). Ułamek jest wysokości H i skali S. Zatem jego baseline (odległość baseline od górnej krawędzi ułamka) to H/2 + B*S.

gosc_z_pytaniem napisał(a)

dla całki elementem baseline będzie pierwsze dziecko
W twoim przykładzie widzę, że w języku MathML nie ma możliwości określenia co jest dzieckiem całki. Jest po prostu znak całki jako jeden z elementów wiersza. Dlatego takie pozycjonowanie odpada.
Nie twórz elementów które nie mają odpowiedników w MathML. W MathML całka nie istnieje, jest tylko symbol całki. Budowa klas powinna pokrywać się z budową MathML.

gosc_z_pytaniem napisał(a)

Tylko teraz jak rozpoznawać, że w tym przypadku baseline to położenie a w tym to kombinacja położenia z wysokością? Zapewne jakaś wirtualna metoda:

virtual const int Baseline() = 0;

Jaka kombinacja? Nie rozumiem. Natomiast wirtualna metoda Baseline jak najbardziej będzie potrzebna do zwracania pozycji baseline względem danego elementu.

0
adf88 napisał(a)
gosc_z_pytaniem napisał(a)

dla całki elementem baseline będzie pierwsze dziecko
W twoim przykładzie widzę, że w języku MathML nie ma możliwości określenia co jest dzieckiem całki. Jest po prostu znak całki jako jeden z elementów wiersza. Dlatego takie pozycjonowanie odpada.
Nie twórz elementów które nie mają odpowiedników w MathML. W MathML całka nie istnieje, jest tylko symbol całki. Budowa klas powinna pokrywać się z budową MathML.

Dokładnie! Tak też klasy rozplanowałem. Słowa całki użyłem jako przykład. Równie dobrze mogłem użyć sumy.

adf88 napisał(a)
gosc_z_pytaniem napisał(a)

I takim elementem baseline np. w ułamku będzie BOTTOM licznika lub TOP mianownika
Dlaczego tak dziwnie? Ułamek ma mieć po prostu kreskę na środku. Aby tak wypozycjonować ułamek trzeba by obliczyć baseline na podstawie wysokości wiersza. Można skorzystać tutaj ze skali o której pisałem wcześniej. Przypuśćmy, że dla wiersza w skali 1 odległość środka wiersza do baseline jest równa B (stała liczba). Ułamek jest wysokości H i skali S. Zatem jego baseline (odległość baseline od górnej krawędzi) to H/2 + B*S.

Dlaczego tak dziwnie? Bo w MathML jeżeli chcę wstawić ułamek to wstawiam między tagi <frac></frac> 2 elementy - licznik i mianownik. Nie ma tam informacji o kresce ułamkowej.

0

Wracając do Twojej uwagi nie mam w projekcie klas odpowiadających za całkę czy sumę. Jest klasa Munderover która odpowiada za wyrażenie ograniczone od góry i od dołu.

0
gosc_z_pytaniem napisał(a)

Dlaczego tak dziwnie? Bo w MathML jeżeli chcę wstawić ułamek to wstawiam między tagi <frac></frac> 2 elementy - licznik i mianownik. Nie ma tam informacji o kresce ułamkowej.
informacją jest sam element <frac> który reprezentuje ułamek.
Zresztą nie to jest istotne. Istotne jest, aby kreska ułamkowa była na środku wiersza. I można to bez problemu wyliczyć tak jak pisałem wcześniej, wewnątrz metody Layout. Z tym, że zły wzór podałem [glowa] . Nie wiedzieć czemu przyjąłem że licznik i mianownik są tak samo wysokie. Poprawka:

B - odległość środka wiersza od baseline'a w skali 1
S - skala w jakiej jest ułamek
K - wyliczona na podstawie rozmiaru dzieci oraz skali odległość kreski ułamkowej od górnej krawędzi całego ułamka.

baseline ułamka (odległość od górnej krawędzi) = K + S*B

0

Chyba jednak trochę swojego kody zaraz wrzucę...

A jeszcze jedna spawa wychodząc poza położenie elementów. W MathML-u za znak całki odpowiada za znak sumy odpowiada itd. Tak kilkadziesiąt albo i więcej znaków.

Mechanizm odpowiadający za rysowanie czyli wszystkie klasy dziedziczące po Expression umieszczam w statycznej bibliotece. Potem dodaje sobie bibliotekę do projektu i jej używam.

I jak teraz w bibliotece przechowywać informacje, że całka to taki ciąg w MathML-u i taki numer ASCII w takiej czcionce. W pliku chyba raczej nie bardzo bo wtedy musiałbym dodawać liba razem z tym plikiem.

0

Unicode użyj obowiązkowo.
Tu masz tabelę: http://www.w3.org/2003/entities/2007doc/#source (mml2.xml - record of MathML 2.0 (second edition) entity definitions)

W programie zrób sobie hashmapę zamieniającą mnemoniki na konkretny znak unicode, możesz ją wypełniać na starcie programu czytając xml'a do którego link podałem.

Zajrzyj też tu: http://www.w3.org/TR/2009/WD-MathML3-20090604/chapter7.html

0

Znów muszę napisać, że jesteś WIELKI!!! Dzięki serdeczne.
Troszkę na Pastebin namieszania narobiłem, ale mam nadzieję że moderatorzy to załatwią.

Postanowiłem tak napisać całą strukturę aby informacje o położeniu i wielkości elementu nie były przechowywane w obiekcie. Tzn. np wywołanie metody GetWidth() ma zwrócić wielkość aktualnego elementu.
No ale oczywiście jest problem z tym baseline. Poniżej zamieszczam link do źródła.
Nie jest to kompletne źródło mojej aplikacji. Po prostu pewne rzeczy było mi łatwiej zrobić najpierw w konsoli i dopiero potem wyrysować na podstawie zwracanych danych. Dlatego ten pliczek który przesyłam zawiera projekt w Dev-CPP i jest aplikacją konsolową, ale myślę że można się sporo z tego dowiedzieć.

<url>www.spjanow.cba.pl/mathml.zip</url>

Aaaaaaa. Oczywiście klasa KMn nie została jeszcze dostosowana do Unicode!

0

Jaki jest problem?

Zdefiniuj sobie w klasie Expression takie chronione pola jak:

  • rozmiar
  • pozycja względna
  • pozycja bezwzględna
  • baseline

Zdefiniuj abstrakcyjną metodę "Layout" której zadaniem jest:
A. Pobranie od wszystkich dzieci ich "rozmiar" oraz "baseline"
B. Na podstawie pobranych wartości ustalenie dzieciom ich "pozycja względna" oraz własny "rozmiar" oraz własny "baseline"

Zdefiniuj metodę "CalcAbsolutePos" która:
A. Ustala "pozycja bezwzględna" elementu na podstawie własnej "pozycja względna" oraz "pozycja bezwzględna" swojego rodzica
B. Wywołuje metodę "CalcAbsolutePos" na każdym dziecku

Powyżej są podstawy mojej koncepcji. Zrób to, a będzie można ruszyć dalej, sama klasa KExpression:

class KExpression
{
protected:
   int left, top;
   int width, height;
   int relative_left, relative_top;
   int baseline;

   string name;
   KExpression* parent;
   vector<KExpression*> child;

   virtual void Layout() = 0;

   //przyda się metodzie Layout
   void SetChildRelPos(int child_idx, int rel_left, rel_top)
   {
      child[child_idx]->relative_left = rel_left;
      child[child_idx]->relative_top = rel_top;
   }

   void CalcAbsolutePos()
   {
      left = parent->left + relative_left;
      top = parent->top + relative_top;
      for (int i = 0; i < child.length(); i++) child[i]->Move();
   }
public:
   KExpression();
   virtual ~KExpression();

   virtual KExpression* Add(KExpression*); 
   virtual KExpression* AddChild(KExpression*);
   KExpression* Parent() const { return parent; }

   int Index() const;

   int Left() const { return left; }
   int Top() const { return left; }
   int Width() const { return width; }
   int Height() const { return height; }
   int Baseline() const { return baseline; }

   string Name() const { return name }
   void Name(const string &new_name) { name = new_name; }
};

PS. przekazując obiekt przez wartość używanie const jest bezsensowne. Zamiast zwracać jako wynik lub pobierać jako argument funkcji zmienną typu "const int" zwracaj/pobieraj "int".

0

Niestety do 21.09 tj. do tego poniedziałku muszę przerwać pisanie projektu, ale od poniedziałku wracam i zaczynam od tego co napisałeś.

Tak na szybko interesują mnie 2 rzeczy, takie bardziej techniczne. Typ std::string ma 4 bajty. Przekazywany jest przez referencję. W żaden sposób referencja nie przyspiesza przekazania parametru bo adres też ma 4 bajty. W takim razie chodzi o to aby pracować na oryginale argumentu. Ale po co??

I druga sprawa. Czy w C++ nie elegancko jest utworzyć publiczne pole czy raczej może zmienną - właściwie zmienną wskaźnikową -> KExpression* Parent. Zauważyłem że zastąpiłeś ją metodą zwracającą wskaźnik. Zapewne to nie spełnia standardów?

A do projektu jak już pisałem wracam w poniedziałek :)

0

Typ std::string ma 4 bajty.

A kto tak powiedział?

W żaden sposób referencja nie przyspiesza przekazania parametru bo adres też ma 4 bajty.

Przyspiesza, bo tu nie tyle chodzi o sam obiekt string, co o pamięć, która ów string zarządza, i w której przechowywany jest tekst. Robiona jest kopia.

I druga sprawa. Czy w C++ nie elegancko jest utworzyć publiczne pole [...]

Nieelegancko.

1 użytkowników online, w tym zalogowanych: 0, gości: 1