面试
很久以前,我面试了很多图形编程职位的候选人。 成为一名优秀的面试官是一项艰苦的工作;我早期的尝试并不好。 随着时间的推移,我想出了这个问题来开始对话。
我:如果同事来找你说“我需要画一个圆圈”,你会怎么做?
- 候选人 0:我不知道。
- 候选人1:我不知道。 指南针?
- 候选人 2:嗯嗯。 我会打印一个小写字母“o”。
- 候选人 3:平方根应该可以。
- 候选 4:正弦和余弦应该可以。
- 候选 5:Bresenham 的圆绘制算法应该可以做到。
- 候选人 6:我会告诉他们寻找库函数,向他们展示文档所在的位置。 重用比发明更好。
- 候选人 7:我会说“我不知道。 你为什么要画一个圆?”因为也许他们并不真的需要画一个。 也许他们需要使用平方根知道圆内有什么。 也许他们需要一个椭圆。 一旦我们确定确实需要绘制一个圆,那么在哪里绘制它呢? 它是填充的、实心的还是有图案的? 很可能已经有一个函数可以做到这一点,并且现在已经调试良好。 您可能不需要自己编写它,但如果您这样做,请使用正弦和余弦,并使步长大小成为半径的函数。 需要多快? 根据设备的不同,Bresenham 可能比较合适。 在打印机上,小写字母“o”可能适用于标记并且足够好。
讨论
候选人 0、1、2、3、4、5 都错过了森林。 如果你是面试官,一定要帮助他们看到问题中的另一个人,因为那是问题的一半。 如果您是候选人,请注意两部分问题。
如果你是面试者,永远不要像 0 号候选人那样停止。 候选人 1 和 2 为面试官提出另一个问题提供了足够的机会。 如果您是面试官,请记住,候选人正处于压力之中,需要一些帮助来了解更多细节。
候选者 3、4 和 5 各自提出了一个技术解决方案,其中任何一个都可能有效,但没有一个总是最好的。 如果您是候选人,请继续谈论您的答案。 如果你是面试官,指导他们:“那会(快速、安全、正确等)吗?”
候选者 6 和候选者 7 在实用尺度上处于不同的位置。 需要更多的问题和讨论来确定最合适的方案。
答案
面试问题实际上与画圆无关,但如果这就是您仍在阅读的原因,这里有 JSL 的答案 2、3、4、5、6。
小写字母 o,三种方式
New Window( "Candidate 2, literal",
Graph Box(
xaxis( Add Ref Line( 50, "Solid", "Black", "", 1 ) ),
yaxis( Add Ref Line( 50, "Solid", "Black", "", 1 ) ),
Text( Center Justified, {50, 50}, "o" ) // "o" sits above the point
)
);
Text sits above the baseline.文本位于基线上方。
New Window( "Candidate 2, modified",
Graph Box(
xaxis( Add Ref Line( 50, "Solid", "Black", "", 1 ) ),
yaxis( Add Ref Line( 50, "Solid", "Black", "", 1 ) ),
markersize(10),
marker( markerstate("O"), {50, 50}) // capital "O" is somewhat centered
)
);
Using a marker with a capital O.使用带有大写 O 的标记。
New Window( "Candidate 2, reinvented",
Graph Box(
xaxis( Add Ref Line( 50, "Solid", "Black", "", 1 ) ),
yaxis( Add Ref Line( 50, "Solid", "Black", "", 1 ) ),
Marker Size( 100 ),
Marker( Marker State( 8 ), {50, 50} ) // bigger is thicker
)
);
If you can use a marker, then you can pick the circle symbol.如果您可以使用标记,那么您可以选择圆形符号。
平方根,两种方式
New Window( "Candidate 3, sqrt hard way",
Graph Box(
framesize( 300, 300 ),
xaxis( Add Ref Line( 50, "Solid", "Black", "", 1 ) ),
yaxis( Add Ref Line( 50, "Solid", "Black", "", 1 ) ),
// draw a circle at 50,50 with radius 30 using sqrt
// a lot of code, and not too pretty on the left and right
step=3; // to make the problem more obvious, use 1 to almost hide it
For( x = 0, x < 30, x += step,
// hyp = sqrt(x^2 + y^2)
// hyp^2 = x^2 + y^2
// hyp^2 - x^2 = y^2
y1 = Sqrt( 30 ^ 2 - x ^ 2 );
y2 = Sqrt( 30 ^ 2 - (x + step) ^ 2 );
// do all four quadrants in parallel
Line( {50 + x, 50 + y1}, {50 + (x + step), 50 + y2} );
Line( {50 - x, 50 + y1}, {50 - (x + step), 50 + y2} );
Line( {50 - x, 50 - y1}, {50 - (x + step), 50 - y2} );
Line( {50 + x, 50 - y1}, {50 + (x + step), 50 - y2} );
)
)
);
The sqrt approach looks rough on the left and right because of the equal-sized steps in x.sqrt 方法在左侧和右侧看起来很粗糙,因为 x 中的步长大小相等。
New Window( "Candidate 3, sqrt with YFunction",
Graph Box(
framesize( 300, 300 ),
xaxis( Add Ref Line( 50, "Solid", "Black", "", 1 ) ),
yaxis( Add Ref Line( 50, "Solid", "Black", "", 1 ) ),
// draw a circle at 50,50 with radius 30 using sqrt
// sqrt has two answers. Looks nice, thanks YFunction. Only a small gap.
Y Function( 50 + Sqrt( 30 ^ 2 - (x - 50) ^ 2 ), x );// top
Y Function( 50 - Sqrt( 30 ^ 2 - (x - 50) ^ 2 ), x );// bottom
)
);
The Y Function adjusts the x step size automatically.Y 函数自动调整 x 步长。
正弦和余弦
New Window( "Candidate 4, sin/cos with XYFunction",
Graph Box(
framesize( 300, 300 ),
xaxis( Add Ref Line( 50, "Solid", "Black", "", 1 ) ),
yaxis( Add Ref Line( 50, "Solid", "Black", "", 1 ) ),
// draw a circle at 50,50 with radius 30 using sin/cos
XY Function( 50 + 30 * Cos( t ), 50 + 30 * Sin( t ), t, Min( 0 ), Max( 2 * Pi() ) )
)
);
I really like the XYFunction for this parametric description of a circle.我真的很喜欢 XYFunction 对圆的参数化描述。
布雷森纳姆
//Bresenham.
// using pixel functions (There is no reason I can think of to
// use this code; everything else will be faster! A long time
// ago there was no floating point arithmetic or sin/cos/sqrt
// and tricks like this were useful.)
drawCircle = Function( {xc, yc, x, y}, // not really pixels. this is not a good match for JMP!
Marker( Marker State( 0 ), {xc + x, yc + y} ); // invented for primitive hardware without
Marker( Marker State( 0 ), {xc - x, yc + y} ); // floating point.
Marker( Marker State( 0 ), {xc + x, yc - y} );
Marker( Marker State( 0 ), {xc - x, yc - y} );
Marker( Marker State( 0 ), {xc + y, yc + x} );
Marker( Marker State( 0 ), {xc - y, yc + x} );
Marker( Marker State( 0 ), {xc + y, yc - x} );
Marker( Marker State( 0 ), {xc - y, yc - x} );
);
circleBres = Function( {xc, yc, r}, // https://www.geeksforgeeks.org/bresenhams-circle-drawing-algorithm/
x = 0;
y = r;
d = 3 - 2 * r;
drawCircle( xc, yc, x, y );
While( y >= x,
x++;
If( d > 0,
y--;
d = d + 4 * (x - y) + 10;
,
d = d + 4 * x + 6
);
drawCircle( xc, yc, x, y );
);
);
New Window( "Candidate 5, really hard way using pixels",
Graph Box(
framesize( 900, 900 ),xscale(0,1000),yscale(0,1000),
xaxis( Add Ref Line( 500, "Solid", "Black", "", 1 ) ),
yaxis( Add Ref Line( 500, "Solid", "Black", "", 1 ) ),
// draw a circle at 500,500 with radius 300
circleBres( 500, 500, 300 ),
text({300,700},"Zoom in for detail")
)
);
The point of the Bresenham circle drawing algorithm is to only use integers and avoid multiply and divide ( *2 and *4 can be shift operations.)Bresenham 圆绘制算法的要点是仅使用整数并避免乘法和除法(*2 和 *4 可以是移位运算。)
Zoomed in the pixels always step in either x or y or both.放大像素始终步进 x 或 y 或两者。
库函数
New Window( "Candidate 6, Circle Function",
Graph Box(
xaxis( Show Major Grid( 1 ) ),
yaxis( Show Major Grid( 1 ) ),
Circle( {50, 50}, 30 )
)
);
The Circle Function uses the Y dimension and makes a visibly round circle. Because the axes are not isometric, something seems odd.圆形函数使用 Y 维度并形成一个明显的圆形。 因为轴不是等轴测的,所以有些东西看起来很奇怪。
New Window( "Candidate 6, Circle on square paper",
Graph Box( framesize(400,400),
xaxis( Show Major Grid( 1 ) ),
yaxis( Show Major Grid( 1 ) ),
Circle( {50, 50}, 30 )
)
);
The graph's popup menu, Size/Scale->SizeToIsometric will automatically make this adjustment.图形的弹出菜单“大小/比例”->“大小到等距”将自动进行此调整。
您可能更喜欢椭圆函数:
New Window( "Candidate 6, Oval function",
Graph Box( // framesize(600,400),
x=50;
y=50;
r=30;
Oval( x-r, y-r, x+r, y+r );
)
);
The Oval function has a clear API for the vertical and horizontal dimensions.Oval 函数对于垂直和水平尺寸有明确的 API。
液压系统
I traced a DVD; the cyborg hand is really teaching about hydraulics and levers and putting pieces together.我追踪了一张 DVD;机器人手实际上是在教授液压系统和杠杆以及将零件组装在一起。
我花了一月和二月帮助一个五岁的孩子组装这个巨型机器人手。 我建议七岁作为合理的最低年龄(盒子也是如此);我们每隔一天左右进行一次 10 到 15 分钟的课程。 大约 30 美元的价格非常超值。 最精彩的部分:有一天,5岁的孩子在回家的路上发现了一台挖掘机,并告诉我有关液压缸的信息。
Huge! Actually too big for a five year old.巨大的! 对于五岁的孩子来说实际上太大了。