How to draw an object and rotate it in oblique frontal (dimetric) projection properly?
An illustration of projection:
I've already made a program (Pascal with Graph unit) which does it, but I think that it draws object incorrectly.
program p7test;
uses PtcCrt, PtcGraph;
type
TPixel = record
x, y, z: real;
end;
TModel = record
p: array [ 1..8 ] of TPixel;
end;
TCenter = record
xc, zc: integer;
end;
var
Driver, Mode: integer;
c: char;
s: string;
ns, rx, ry, rz, ra, m_l, m_w, m_h, m_l_d, m_w_d, m_h_d: integer;
model_d, model: TModel;
center: TCenter;
procedure LineXYZ( sp_t, ep_t: TPixel; center_t: TCenter );
var
x1, y1, x2, y2: real;
begin
x1 := sp_t.x - sin( pi / 4 ) * sp_t.y / 2;
y1 := sp_t.z - sin( pi / 4 ) * sp_t.y / 2;
x2 := ep_t.x - sin( pi / 4 ) * ep_t.y / 2;
y2 := ep_t.z - sin( pi / 4 ) * ep_t.y / 2;
Line(
round( center_t.xc - x1 ),
round( center_t.zc - y1 ),
round( center_t.xc - x2 ),
round( center_t.zc - y2 )
);
end;
procedure DrawModel( model_t: TModel; center_t: TCenter );
var
i: integer;
begin
LineXYZ( model_t.p[ 1 ], model_t.p[ 2 ], center_t );
LineXYZ( model_t.p[ 2 ], model_t.p[ 3 ], center_t );
LineXYZ( model_t.p[ 3 ], model_t.p[ 4 ], center_t );
LineXYZ( model_t.p[ 4 ], model_t.p[ 1 ], center_t );
LineXYZ( model_t.p[ 5 ], model_t.p[ 6 ], center_t );
LineXYZ( model_t.p[ 6 ], model_t.p[ 7 ], center_t );
LineXYZ( model_t.p[ 7 ], model_t.p[ 8 ], center_t );
LineXYZ( model_t.p[ 8 ], model_t.p[ 5 ], center_t );
LineXYZ( model_t.p[ 1 ], model_t.p[ 5 ], center_t );
LineXYZ( model_t.p[ 2 ], model_t.p[ 6 ], center_t );
LineXYZ( model_t.p[ 3 ], model_t.p[ 7 ], center_t );
LineXYZ( model_t.p[ 4 ], model_t.p[ 8 ], center_t );
end;
function RotateZ( model_t: TModel; angle: real ): TModel;
var
x, y: real;
i: integer;
begin
angle := angle * pi / 180;
for i := 1 to 8 do
begin
x := model_t.p[ i ].x;
y := model_t.p[ i ].y;
model_t.p[ i ].x := x * cos( angle ) - y * sin( angle );
model_t.p[ i ].y := y * cos( angle ) + x * sin( angle );
end;
RotateZ := model_t;
end;
function RotateY( model_t: TModel; angle: real ): TModel;
var
x, z: real;
i: integer;
begin
angle := angle * pi / 180;
for i := 1 to 8 do
begin
x := model_t.p[ i ].x;
z := model_t.p[ i ].z;
model_t.p[ i ].x := x * cos( angle ) - z * sin( angle );
model_t.p[ i ].z := z * cos( angle ) + x * sin( angle );
end;
RotateY := model_t;
end;
function RotateX( model_t: TModel; angle: real ): TModel;
var
y, z: real;
i: integer;
begin
angle := angle * pi / 180;
for i := 1 to 8 do
begin
y := model_t.p[ i ].y;
z := model_t.p[ i ].z;
model_t.p[ i ].y := y * cos( angle ) - z * sin( angle );
model_t.p[ i ].z := z * cos( angle ) + y * sin( angle );
end;
RotateX := model_t;
end;
function RotateXYZ( model_t: TModel; rx_t, ry_t, rz_t: integer ): TModel;
begin
model_t := RotateX( model_t, rx_t );
model_t := RotateY( model_t, ry_t );
model_t := RotateZ( model_t, rz_t );
RotateXYZ := model_t;
end;
begin
Driver := D8bit;
Mode := m800x600;
InitGraph( Driver, Mode, '' );
ra := 2;
if ( GraphResult <> GrOk ) then WriteLn( '640x480x256''s not supported' ) else
begin
ClearDevice;
center.xc := ( GetMaxX div 2 ) + 1;
center.zc := ( GetMaxY div 2 ) + 1;
m_l_d := 200; m_w_d := 200; m_h_d := 200;
m_l := m_l_d; m_w := m_w_d; m_h := m_h_d;
rx := -26; ry := 6; rz := 16;
model_d.p[ 1 ].x := - m_l / 2; model_d.p[ 1 ].y := - m_w / 2; model_d.p[ 1 ].z := - m_h / 2;
model_d.p[ 2 ].x := - m_l / 2; model_d.p[ 2 ].y := m_w / 2; model_d.p[ 2 ].z := - m_h / 2;
model_d.p[ 3 ].x := m_l / 2; model_d.p[ 3 ].y := m_w / 2; model_d.p[ 3 ].z := - m_h / 2;
model_d.p[ 4 ].x := m_l / 2; model_d.p[ 4 ].y := - m_w / 2; model_d.p[ 4 ].z := - m_h / 2;
model_d.p[ 5 ].x := - m_l / 2; model_d.p[ 5 ].y := - m_w / 2; model_d.p[ 5 ].z := m_h / 2;
model_d.p[ 6 ].x := - m_l / 2; model_d.p[ 6 ].y := m_w / 2; model_d.p[ 6 ].z := m_h / 2;
model_d.p[ 7 ].x := m_l / 2; model_d.p[ 7 ].y := m_w / 2; model_d.p[ 7 ].z := m_h / 2;
model_d.p[ 8 ].x := m_l / 2; model_d.p[ 8 ].y := - m_w / 2; model_d.p[ 8 ].z := m_h / 2;
model := RotateXYZ( model_d, rx, ry, rz );
SetColor( 2 ); DrawModel( model, center );
SetColor( 12 );
Str( rx, s ); OutTextXY( 2, 2, 'rx=' + s );
Str( ry, s ); OutTextXY( 2, 12, 'ry=' + s );
Str( rz, s ); OutTextXY( 2, 22, 'rz=' + s );
repeat Delay( 100 ) until KeyPressed;
if ns = 0 then ns := 1 else ns := 0;
ReadKey;
repeat
c := ReadKey;
case c of
#113: begin rx := rx - ra; model := RotateXYZ( model_d, rx, ry, rz ); end;
#101: begin rx := rx + ra; model := RotateXYZ( model_d, rx, ry, rz ); end;
#119: begin ry := ry - ra; model := RotateXYZ( model_d, rx, ry, rz ); end;
#115: begin ry := ry + ra; model := RotateXYZ( model_d, rx, ry, rz ); end;
#97: begin rz := rz - ra; model := RotateXYZ( model_d, rx, ry, rz ); end;
#100: begin rz := rz + ra; model := RotateXYZ( model_d, rx, ry, rz ); end;
#117: begin
rx := 0; ry := 0; rz := 0;
model := RotateXYZ( model_d, rx, ry, rz );
end;
end;
ClearDevice;
SetColor( 2 ); DrawModel( model, center );
SetColor( 12 );
Str( rx, s ); OutTextXY( 2, 2, 'rx=' + s );
Str( ry, s ); OutTextXY( 2, 12, 'ry=' + s );
Str( rz, s ); OutTextXY( 2, 22, 'rz=' + s );
if ns = 0 then
begin
SetActivePage(0);
SetVisualPage(1)
end
else
begin
SetActivePage(1);
SetVisualPage(0)
end;
if ns = 0 then ns := 1 else ns := 0;
until c = #27;
CloseGraph;
end;
end.
You might use WASDQER keys to rotate an object.
So, as you might notice in animation below, there's some issue when you're looking at it and it's slightly elongated:
Shouldn't it look like one below?:
I tried to change LineXYZ() code lines to this:
x1 := sp_t.x - ( sp_t.y / 2 );
y1 := sp_t.z - ( sp_t.y / 2 );
x2 := ep_t.x - ( ep_t.y / 2 );
y2 := ep_t.z - ( ep_t.y / 2 );
, but it might be incorrect, too.
Am I rotating x, y, z coordinates with functions like (Rotate*) correctly?
By the way, I think that the main problem is a LineXYZ() function(y coordinate part).
How to draw an object in this type of projection?
Thank you very much, as needed.
Best regards,
V7
Isn't it should look like one below?
Not, gif below is 90-90 projection, while your axis are 135-90
Your code is correct, except two things:
Your X axis is wrong directed
You use sin instead of cos, while angle is pi/4 they're same, but if you plan to change angle...
Your code:
x1 := sp_t.x - sin( pi / 4 ) * sp_t.y / 2;
y1 := sp_t.z - sin( pi / 4 ) * sp_t.y / 2;
x2 := ep_t.x - sin( pi / 4 ) * ep_t.y / 2;
y2 := ep_t.z - sin( pi / 4 ) * ep_t.y / 2;
Should be:
x1 := -sp_t.x - cos( pi / 4 ) * sp_t.y / 2;
y1 := sp_t.z - sin( pi / 4 ) * sp_t.y / 2;
x2 := -ep_t.x - cos( pi / 4 ) * ep_t.y / 2;
y2 := ep_t.z - sin( pi / 4 ) * ep_t.y / 2;
In rest your render is correct.
Live Demo
About #lurker notes:
No reason to fix perspective illusion here (it's something personificated - impossible to compensate it for all viewers equally).
This gif just demonstrate this illusion effect:
Related
I have some code on VHDL language, this code male some encryption/decryption operation. Help me please make this code be synthesizable because Xilinx IDE told me that
line 82: Operator must have constant modulo operand.
This is my code. Maybe you have some wish for me how I can refactor it.
library ieee;
use ieee.std_logic_1164.all;
use ieee.STD_LOGIC_TEXTIO.all;
use ieee.STD_LOGIC_UNSIGNED.all;
use ieee.STD_LOGIC_SIGNED.all;
use ieee.NUMERIC_STD.all;
use ieee.NUMERIC_BIT.all;
use ieee.std_logic_arith.all;
use ieee.MATH_REAL.all;
use ieee.MATH_COMPLEX.all;
entity comp is
port(
clk : in STD_LOGIC;
word : in INTEGER;
n : inout INTEGER;
v1 : out INTEGER;
v2 : out INTEGER;
v3 : out INTEGER;
v4 : out INTEGER;
v5 : out INTEGER;
v6 : out INTEGER;
v7 : out INTEGER;
v8 : out INTEGER
);
end comp;
architecture arch of comp is
---- Signal declarations used on the diagram ----
signal g1 : INTEGER := 1;
signal g2 : INTEGER := 1;
signal g3 : INTEGER := 1;
signal k1 : INTEGER := 0;
signal k2 : INTEGER := 0;
signal k3 : INTEGER := 0;
signal m1 : INTEGER;
signal m2 : INTEGER;
signal m3 : INTEGER;
---signal n : INTEGER;
signal p1 : INTEGER;
signal p1_g1 : INTEGER;
signal p2 : INTEGER;
signal p2_g2 : INTEGER;
signal p3 : INTEGER;
signal p3_g3 : INTEGER;
signal sqrt1 : INTEGER;
signal sqrt2 : INTEGER;
signal sqrt3 : INTEGER;
signal w : INTEGER;
---signal word : INTEGER;
begin
---- Processes ----
read : process (clk)
begin
if clk'event and clk = '1' then
n <= p1 * p2 * p3;
w <= word * word MOD n;
sqrt1 <= w MOD p1;
sqrt2 <= w MOD p2;
sqrt3 <= w MOD p3;
if k1*k1 MOD p1 /= sqrt1 then
k1 <= k1 + 1;
else
g1 <= k1;
end if;
if k2*k2 MOD p2 /= sqrt2 then
k2 <= k2 + 1;
else
g2 <= k2;
end if;
if k3*k3 MOD p3 /= sqrt3 then
k3 <= k3 + 1;
else
g3 <= k3;
end if;
p1_g1 <= p1 - g1;
p2_g2 <= p2 - g2;
p3_g3 <= p3 - g3;
m1 <= n / p1;
m2 <= n / p2;
m3 <= n / p3;
v1 <= (-m1 * g1 + m2 * g2 + m3 * g3) MOD n;
v2 <= (-m1 * g1 + m2 * g2 + m3 * p3_g3) MOD n;
v3 <= (-m1 * g1 + m2 * p2_g2 + m3 * g3) MOD n;
v4 <= (-m1 * g1 + m2 * p2_g2 + m3 * p3_g3) MOD n;
v5 <= (-m1 * p1_g1 + m2 * g2 + m3 * g3) MOD n;
v6 <= (-m1 * p1_g1 + m2 * g2 + m3 * p3_g3) MOD n;
v7 <= (-m1 * p1_g1 + m2 * p2_g2 + m3 * g3) MOD n;
v8 <= (-m1 * p1_g1 + m2 * p2_g2 + m3 * p3_g3) MOD n;
end if;
end process;
end arch;
In VHDL/verilog MOD ,REM and DIVISION operators are not synthesizable. Division is only possible when the second operand is a power of 2.
even if you want to traverse your code using a loop to find the remainder you need to put a constant in the condition segment to get your code synthesized.
I'm trying to create a program that will capture a windows x,y position and w,h ratios compared to the monitor it happens to be on. Then, the program will hide the window until it is released. It will release on the monitor that the program's main gui has been moved to when a button is clicked. When the window is released, it will show and position itself in the same way it originally was, just on the new monitor. So far, everything is working except the position and size pieces.
I'm using only the work areas of the monitors and I have 3 monitors set up. Their coordinates are as follows:
1 (primary center monitor)
Left: 0
Top: 0
Right: 1366
Bottom: 738
2 (right monitor)
Left: 1366
Top: -652
Right: 3286
Bottom: 398
3 (left monitor)
Left: -1920
Top: -864
Right: 0
Bottom: 186
For a visual representation, here's what they look like virtually:
As you can see, the left and right monitors are 1920x1080, however my middle one is actually a laptop and has a smaller resolution at 1366x768
Code-wise, I've tried several things but the latest attempt is as follows:
When storing the values,
Sysget monstart, Monitor, %currentMonitorIndex%
WinGetPos, answerWinx, answerWiny, answerWinw, answerWinh, %title%
if( monstartLeft <= 0 ) ; check if this is the left monitor and has negative coordinates
{
if( monstartRight = 0 ) ; change 0 to 1 to prevent dividing by 0
{
monstartRight = 1
}
; get ratios for the position and size relative to monitor
ratiox := answerWinx / monstartRight
ratioy := answerWiny / monstartBottom
ratiow := ( answerWinx + answerWinw ) / monstartRight
ratioh := ( answerWiny + answerWinh ) / monstartBottom
}
else
{
ratiox := answerWinx / monstartLeft
ratioy := answerWiny / monstartBottom
ratiow := ( answerWinx + answerWinw ) / monstartLeft
ratioh := ( answerWiny + answerWinh ) / monstartBottom
}
Then when trying to restore the window:
cubeIndex := GetMonitorIndexFromWindow( hGui1 )
SysGet, monit, Monitor, %cubeIndex%
monitWidth := monitRight - monitLeft
monitHeight := monitBottom - monitTop
if( monitLeft >= 0 )
{
winNewX := monitLeft + ( ratiox * ( monitRight - monitLeft ) )
winNewY := monitTop + ( ratioy * ( monitBottom - monitTop ) )
winNewW := ratiow * monitRight
winNewH := ratioh * monitBottom
winNewWidth := winNewW - winNewX
winNewHeight := winNewH - winNewY
}
else
{
winNewX := ratiox * monitLeft
winNewY := ratioy * monitBottom
winNewW := ratiow * monitLeft
winNewH := ratioh * monitBottom
winNewWidth := abs( winNewW - winNewX )
winNewHeight := abs( winNewH - winNewY )
}
ratiox := ratiox
ratioy := ratioy
ratiow := ratiow
ratioh := ratioh
WinMove, %title%,, winNewX, winNewY, winNewWidth, winNewHeight
WinActivate, %title%
I had it working before when I hid and restored on the same monitor. Since then I've changed it and now it makes the window reappear off monitor. I've been staring at this and flipping values around for so long I'm confusing myself. I just need a second pair of eyes on this if you have the time.
Thanks in advance.
Edit Following the link posted below, the code that ended up working perfectly is as follows:
For storing the values:
ratiox := ( answerWinx - monstartLeft ) / ( monstartRight - monstartLeft )
ratioy := ( answerWiny - monstartTop ) / ( monstartBottom - monstartTop )
ratiow := ( ( answerWinx + answerWinw ) - monstartLeft ) / ( monstartRight - monstartLeft )
ratioh := ( ( answerWiny + answerWinh ) - monstartTop ) / ( monstartBottom - monstartTop )
and for retrieving them:
winNewX := monitLeft + ( monitRight - monitLeft ) * ratiox
winNewY := monitTop + ( monitBottom - monitTop ) * ratioy
winNewW := monitLeft + ( monitRight - monitLeft ) * ratiow
winNewH := monitTop + ( monitBottom - monitTop ) * ratioh
winNewWidth := winNewW - winNewX
winNewHeight := winNewH - winNewY
As per Forivin's suggestion, here is the answer I found
For storing the values:
ratiox := ( answerWinx - monstartLeft ) / ( monstartRight - monstartLeft )
ratioy := ( answerWiny - monstartTop ) / ( monstartBottom - monstartTop )
ratiow := ( ( answerWinx + answerWinw ) - monstartLeft ) / ( monstartRight - monstartLeft )
ratioh := ( ( answerWiny + answerWinh ) - monstartTop ) / ( monstartBottom - monstartTop )
and for retrieving them:
winNewX := monitLeft + ( monitRight - monitLeft ) * ratiox
winNewY := monitTop + ( monitBottom - monitTop ) * ratioy
winNewW := monitLeft + ( monitRight - monitLeft ) * ratiow
winNewH := monitTop + ( monitBottom - monitTop ) * ratioh
winNewWidth := winNewW - winNewX
winNewHeight := winNewH - winNewY
I have 3 points containing X, Y, Z coordinates:
var A = {x: 100, y: 100, z: 80},
B = {x: 100, y: 175, z: 80},
C = {x: 100, y: 100, z: 120};
The coordinates are pixels from a 3d CSS transform.
How can I get the angle between vectors BA and BC?
A math formula will do, JavaScript code will be better.
Thank you.
In pseudo-code, the vector BA (call it v1) is:
v1 = {A.x - B.x, A.y - B.y, A.z - B.z}
Similarly the vector BC (call it v2) is:
v2 = {C.x - B.x, C.y - B.y, C.z - B.z}
The dot product of v1 and v2 is a function of the cosine of the angle between them (it's scaled by the product of their magnitudes). So first normalize v1 and v2:
v1mag = sqrt(v1.x * v1.x + v1.y * v1.y + v1.z * v1.z)
v1norm = {v1.x / v1mag, v1.y / v1mag, v1.z / v1mag}
v2mag = sqrt(v2.x * v2.x + v2.y * v2.y + v2.z * v2.z)
v2norm = {v2.x / v2mag, v2.y / v2mag, v2.z / v2mag}
Then calculate the dot product:
res = v1norm.x * v2norm.x + v1norm.y * v2norm.y + v1norm.z * v2norm.z
And finally, recover the angle:
angle = acos(res)
double GetAngleABC( double* a, double* b, double* c )
{
double ab[3] = { b[0] - a[0], b[1] - a[1], b[2] - a[2] };
double bc[3] = { c[0] - b[0], c[1] - b[1], c[2] - b[2] };
double abVec = sqrt(ab[0] * ab[0] + ab[1] * ab[1] + ab[2] * ab[2]);
double bcVec = sqrt(bc[0] * bc[0] + bc[1] * bc[1] + bc[2] * bc[2]);
double abNorm[3] = {ab[0] / abVec, ab[1] / abVec, ab[2] / abVec};
double bcNorm[3] = {bc[0] / bcVec, bc[1] / bcVec, bc[2] / bcVec};
double res = abNorm[0] * bcNorm[0] + abNorm[1] * bcNorm[1] + abNorm[2] * bcNorm[2];
return acos(res)*180.0/ 3.141592653589793;
}
double a[] = {1, 0, 0};
double b[] = {0, 0, 0};
double c[] = {0, 1, 0};
std::cout<< "The angle of ABC is " << GetAngleABC(a,b,c)<< "ยบ " << std::endl;
#Roger algorithm in swift
func SCNVector3Angle(start: SCNVector3, mid: SCNVector3, end: SCNVector3) -> Double {
let v1 = (start - mid)
let v2 = (end - mid)
let v1norm = v1.normalized()
let v2norm = v2.normalized()
let res = v1norm.x * v2norm.x + v1norm.y * v2norm.y + v1norm.z * v2norm.z
let angle: Double = Double(GLKMathRadiansToDegrees(acos(res)))
return angle
}
/**
* Subtracts two SCNVector3 vectors and returns the result as a new SCNVector3.
*/
func - (left: SCNVector3, right: SCNVector3) -> SCNVector3 {
return SCNVector3Make(left.x - right.x, left.y - right.y, left.z - right.z)
}
extension SCNVector3
{
/**
* Returns the length (magnitude) of the vector described by the SCNVector3
*/
func length() -> Float {
return sqrtf(x*x + y*y + z*z)
}
/**
* Normalizes the vector described by the SCNVector3 to length 1.0 and returns
* the result as a new SCNVector3.
*/
func normalized() -> SCNVector3 {
return self / length()
}
}
The same in python (with output in degrees):
import numpy as np
import math
import time
def angle_2p_3d(a, b, c):
v1 = np.array([ a[0] - b[0], a[1] - b[1], a[2] - b[2] ])
v2 = np.array([ c[0] - b[0], c[1] - b[1], c[2] - b[2] ])
v1mag = np.sqrt([ v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2] ])
v1norm = np.array([ v1[0] / v1mag, v1[1] / v1mag, v1[2] / v1mag ])
v2mag = np.sqrt(v2[0] * v2[0] + v2[1] * v2[1] + v2[2] * v2[2])
v2norm = np.array([ v2[0] / v2mag, v2[1] / v2mag, v2[2] / v2mag ])
res = v1norm[0] * v2norm[0] + v1norm[1] * v2norm[1] + v1norm[2] * v2norm[2]
angle_rad = np.arccos(res)
return math.degrees(angle_rad)
p1 = np.array([1,0,0])
p2 = np.array([0,0,0])
p3 = np.array([0,0,1])
start = time.time()
angle= angle_2p_3d(p1, p2, p3)
end = time.time()
print("angle: ", angle)
print("elapsed in: ", end - start)
Output:
angle: 90.0
elapsed in: 8.392333984375e-05
How can I calculate
the luminosity contrast ratio,
the difference in brightness, and
the difference in color
between two given colors?
Example:
Input
color 1 : #99ccff
color 2: #225588
Output
luminosity contrast ratio : 4.57 : 1
difference in brightness : 119
difference in color : 357
Convert each colour from (R, G, B) to (H, S, V) [or (H, S, L)] coordinates. You can find formulae at Wikipedia.
I have written Pascal functions for this:
function RGBToHSV(const Color: TRGB): THSV;
var
cmax, cmin, cdiff: real;
begin
cmax := MaxComponent(Color);
cmin := MinComponent(Color);
cdiff := cmax - cmin;
with Color, result do
begin
// Hue
if cmax = cmin then
hsvHue := 0
else if cmax = rgbRed then
hsvHue := (60 * (rgbGreen - rgbBlue) / cdiff)
else if cmax = rgbGreen then
hsvHue := (60 * (rgbBlue - rgbRed) / cdiff) + 120
else
hsvHue := (60 * (rgbRed - rgbGreen) / cdiff) + 240;
hsvHue := Fix360(hsvHue);
// Saturation
if cmax = 0 then
hsvSaturation := 0
else
hsvSaturation := 1 - cmin / cmax;
// Value
hsvValue := cmax;
end;
end;
function RGBToHSL(const Color: TRGB): THSL;
var
cmax, cmin, cdiff, csum: real;
begin
cmax := MaxComponent(Color);
cmin := MinComponent(Color);
cdiff := cmax - cmin;
csum := cmax + cmin;
with Color, result do
begin
// Hue
if cmax = cmin then
hslHue := 0
else if cmax = rgbRed then
hslHue := (60 * (rgbGreen - rgbBlue) / cdiff)
else if cmax = rgbGreen then
hslHue := (60 * (rgbBlue - rgbRed) / cdiff) + 120
else
hslHue := (60 * (rgbRed - rgbGreen) / cdiff) + 240;
hslHue := Fix360(hslHue);
// Saturation
if cmax = cmin then
hslSaturation := 0
else if csum <= 1 then
hslSaturation := cdiff / csum
else
hslSaturation := cdiff / (2 - csum);
// Lightness
hslLightness := csum / 2;
end;
end;
What is the formula to draw a line in x, y space given b-spline coordinates in a TrueType font?
Please note that Truetype allows for bspline curves AND straight lines within a glyph definition.
If you need to change these commands into a series of MoveTo's and LineTo's you can use the following:
Convert the TrueType data into a list of hinted coordinates. This is something your OS can do for you (following code uses the Windows APIs).
Walk all coordinates and translate the curves into lines (see code sniplet below):
procedure TGlyphEvaluator.EvaluateFromBuffer( Action: TGlyphEvaluatorAction );
var
H : TTPOLYGONHEADER;
C : TTPOLYCURVE;
Points : array of TPointFX;
P, PE : DWORD;
i, j : Integer;
F : Double;
PA, PB, PC : TPoint;
begin
SetLength( Points, 10 );
P := 0;
repeat
// Eat the polygon header
Move( FBuffer[ P ], H, sizeof( H ) );
if H.dwType <> TT_POLYGON_TYPE then Break; // Sanity check!
PE := P + H.cb;
Inc( P, sizeof( H ) );
Points[ 0 ] := H.pfxStart;
// Eat all the curve records
while P < PE do begin
// Get the curve record
Move( FBuffer[ P ], C, sizeof( C ) - sizeof( TPointFX ) );
Inc( P, sizeof( C ) - sizeof( TPointFX ) );
// Get the points from the curve record
if Length( Points ) < C.cpfx + 1 then Setlength( Points, C.cpfx + 1 );
Move( FBuffer[ P ], Points[ 1 ], sizeof( TPointFX ) * C.cpfx );
Inc( P, sizeof( TPointFX ) * C.cpfx );
case C.wType of
TT_PRIM_LINE: begin
MoveTo( Action, Points[ 0 ].x.value, Points[ 0 ].y.value );
for i := 1 to C.cpfx do
LineTo( Action, Points[ i ].x.value, Points[ i ].y.value );
end;
TT_PRIM_QSPLINE: begin
MoveTo( Action, Points[ 0 ].x.value, Points[ 0 ].y.value );
PA.X := Points[ 0 ].x.value;
PA.Y := Points[ 0 ].y.value;
for i := 1 to C.cpfx - 1 do begin // DrawQSpline is called C.cpfx - 1 times
PB.X := Points[ i ].x.value;
PB.Y := Points[ i ].y.value;
PC.X := Points[ i + 1 ].x.value;
PC.Y := Points[ i + 1 ].y.value;
if i < C.cpfx - 1 then begin
PC.X := ( PC.X + PB.X ) div 2;
PC.Y := ( PC.Y + PB.Y ) div 2;
end;
for j := 1 to 8 do begin
F := j / 8;
LineTo( Action, Round( ( PA.x - 2 * PB.x + PC.x ) * Sqr( F ) + ( 2 * PB.x - 2 * PA.x ) * F + PA.x ),
Round( ( PA.y - 2 * PB.y + PC.y ) * Sqr( F ) + ( 2 * PB.y - 2 * PA.y ) * F + PA.y ) );
end;
PA := PC;
end;
end;
end;
// Update last point.
Points[ 0 ] := Points[ C.cpfx ];
end;
MoveTo( Action, Points[ 0 ].x.value, Points[ 0 ].y.value );
LineTo( Action, H.pfxStart.x.value, H.pfxStart.y.value );
until P >= Longword( Length( FBuffer ) );
end;